about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/numpy/_typing
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/numpy/_typing')
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/_typing/__init__.py221
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/_typing/_add_docstring.py152
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/_typing/_array_like.py167
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/_typing/_callable.pyi338
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/_typing/_char_codes.py111
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/_typing/_dtype_like.py246
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/_typing/_extended_precision.py27
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/_typing/_nbit.py16
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/_typing/_nested_sequence.py86
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/_typing/_scalars.py30
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/_typing/_shape.py7
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/_typing/_ufunc.pyi445
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/_typing/setup.py10
13 files changed, 1856 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/numpy/_typing/__init__.py b/.venv/lib/python3.12/site-packages/numpy/_typing/__init__.py
new file mode 100644
index 00000000..29922d95
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/_typing/__init__.py
@@ -0,0 +1,221 @@
+"""Private counterpart of ``numpy.typing``."""
+
+from __future__ import annotations
+
+from .. import ufunc
+from .._utils import set_module
+from typing import TYPE_CHECKING, final
+
+
+@final  # Disallow the creation of arbitrary `NBitBase` subclasses
+@set_module("numpy.typing")
+class NBitBase:
+    """
+    A type representing `numpy.number` precision during static type checking.
+
+    Used exclusively for the purpose static type checking, `NBitBase`
+    represents the base of a hierarchical set of subclasses.
+    Each subsequent subclass is herein used for representing a lower level
+    of precision, *e.g.* ``64Bit > 32Bit > 16Bit``.
+
+    .. versionadded:: 1.20
+
+    Examples
+    --------
+    Below is a typical usage example: `NBitBase` is herein used for annotating
+    a function that takes a float and integer of arbitrary precision
+    as arguments and returns a new float of whichever precision is largest
+    (*e.g.* ``np.float16 + np.int64 -> np.float64``).
+
+    .. code-block:: python
+
+        >>> from __future__ import annotations
+        >>> from typing import TypeVar, TYPE_CHECKING
+        >>> import numpy as np
+        >>> import numpy.typing as npt
+
+        >>> T1 = TypeVar("T1", bound=npt.NBitBase)
+        >>> T2 = TypeVar("T2", bound=npt.NBitBase)
+
+        >>> def add(a: np.floating[T1], b: np.integer[T2]) -> np.floating[T1 | T2]:
+        ...     return a + b
+
+        >>> a = np.float16()
+        >>> b = np.int64()
+        >>> out = add(a, b)
+
+        >>> if TYPE_CHECKING:
+        ...     reveal_locals()
+        ...     # note: Revealed local types are:
+        ...     # note:     a: numpy.floating[numpy.typing._16Bit*]
+        ...     # note:     b: numpy.signedinteger[numpy.typing._64Bit*]
+        ...     # note:     out: numpy.floating[numpy.typing._64Bit*]
+
+    """
+
+    def __init_subclass__(cls) -> None:
+        allowed_names = {
+            "NBitBase", "_256Bit", "_128Bit", "_96Bit", "_80Bit",
+            "_64Bit", "_32Bit", "_16Bit", "_8Bit",
+        }
+        if cls.__name__ not in allowed_names:
+            raise TypeError('cannot inherit from final class "NBitBase"')
+        super().__init_subclass__()
+
+
+# Silence errors about subclassing a `@final`-decorated class
+class _256Bit(NBitBase):  # type: ignore[misc]
+    pass
+
+class _128Bit(_256Bit):  # type: ignore[misc]
+    pass
+
+class _96Bit(_128Bit):  # type: ignore[misc]
+    pass
+
+class _80Bit(_96Bit):  # type: ignore[misc]
+    pass
+
+class _64Bit(_80Bit):  # type: ignore[misc]
+    pass
+
+class _32Bit(_64Bit):  # type: ignore[misc]
+    pass
+
+class _16Bit(_32Bit):  # type: ignore[misc]
+    pass
+
+class _8Bit(_16Bit):  # type: ignore[misc]
+    pass
+
+
+from ._nested_sequence import (
+    _NestedSequence as _NestedSequence,
+)
+from ._nbit import (
+    _NBitByte as _NBitByte,
+    _NBitShort as _NBitShort,
+    _NBitIntC as _NBitIntC,
+    _NBitIntP as _NBitIntP,
+    _NBitInt as _NBitInt,
+    _NBitLongLong as _NBitLongLong,
+    _NBitHalf as _NBitHalf,
+    _NBitSingle as _NBitSingle,
+    _NBitDouble as _NBitDouble,
+    _NBitLongDouble as _NBitLongDouble,
+)
+from ._char_codes import (
+    _BoolCodes as _BoolCodes,
+    _UInt8Codes as _UInt8Codes,
+    _UInt16Codes as _UInt16Codes,
+    _UInt32Codes as _UInt32Codes,
+    _UInt64Codes as _UInt64Codes,
+    _Int8Codes as _Int8Codes,
+    _Int16Codes as _Int16Codes,
+    _Int32Codes as _Int32Codes,
+    _Int64Codes as _Int64Codes,
+    _Float16Codes as _Float16Codes,
+    _Float32Codes as _Float32Codes,
+    _Float64Codes as _Float64Codes,
+    _Complex64Codes as _Complex64Codes,
+    _Complex128Codes as _Complex128Codes,
+    _ByteCodes as _ByteCodes,
+    _ShortCodes as _ShortCodes,
+    _IntCCodes as _IntCCodes,
+    _IntPCodes as _IntPCodes,
+    _IntCodes as _IntCodes,
+    _LongLongCodes as _LongLongCodes,
+    _UByteCodes as _UByteCodes,
+    _UShortCodes as _UShortCodes,
+    _UIntCCodes as _UIntCCodes,
+    _UIntPCodes as _UIntPCodes,
+    _UIntCodes as _UIntCodes,
+    _ULongLongCodes as _ULongLongCodes,
+    _HalfCodes as _HalfCodes,
+    _SingleCodes as _SingleCodes,
+    _DoubleCodes as _DoubleCodes,
+    _LongDoubleCodes as _LongDoubleCodes,
+    _CSingleCodes as _CSingleCodes,
+    _CDoubleCodes as _CDoubleCodes,
+    _CLongDoubleCodes as _CLongDoubleCodes,
+    _DT64Codes as _DT64Codes,
+    _TD64Codes as _TD64Codes,
+    _StrCodes as _StrCodes,
+    _BytesCodes as _BytesCodes,
+    _VoidCodes as _VoidCodes,
+    _ObjectCodes as _ObjectCodes,
+)
+from ._scalars import (
+    _CharLike_co as _CharLike_co,
+    _BoolLike_co as _BoolLike_co,
+    _UIntLike_co as _UIntLike_co,
+    _IntLike_co as _IntLike_co,
+    _FloatLike_co as _FloatLike_co,
+    _ComplexLike_co as _ComplexLike_co,
+    _TD64Like_co as _TD64Like_co,
+    _NumberLike_co as _NumberLike_co,
+    _ScalarLike_co as _ScalarLike_co,
+    _VoidLike_co as _VoidLike_co,
+)
+from ._shape import (
+    _Shape as _Shape,
+    _ShapeLike as _ShapeLike,
+)
+from ._dtype_like import (
+    DTypeLike as DTypeLike,
+    _DTypeLike as _DTypeLike,
+    _SupportsDType as _SupportsDType,
+    _VoidDTypeLike as _VoidDTypeLike,
+    _DTypeLikeBool as _DTypeLikeBool,
+    _DTypeLikeUInt as _DTypeLikeUInt,
+    _DTypeLikeInt as _DTypeLikeInt,
+    _DTypeLikeFloat as _DTypeLikeFloat,
+    _DTypeLikeComplex as _DTypeLikeComplex,
+    _DTypeLikeTD64 as _DTypeLikeTD64,
+    _DTypeLikeDT64 as _DTypeLikeDT64,
+    _DTypeLikeObject as _DTypeLikeObject,
+    _DTypeLikeVoid as _DTypeLikeVoid,
+    _DTypeLikeStr as _DTypeLikeStr,
+    _DTypeLikeBytes as _DTypeLikeBytes,
+    _DTypeLikeComplex_co as _DTypeLikeComplex_co,
+)
+from ._array_like import (
+    NDArray as NDArray,
+    ArrayLike as ArrayLike,
+    _ArrayLike as _ArrayLike,
+    _FiniteNestedSequence as _FiniteNestedSequence,
+    _SupportsArray as _SupportsArray,
+    _SupportsArrayFunc as _SupportsArrayFunc,
+    _ArrayLikeInt as _ArrayLikeInt,
+    _ArrayLikeBool_co as _ArrayLikeBool_co,
+    _ArrayLikeUInt_co as _ArrayLikeUInt_co,
+    _ArrayLikeInt_co as _ArrayLikeInt_co,
+    _ArrayLikeFloat_co as _ArrayLikeFloat_co,
+    _ArrayLikeComplex_co as _ArrayLikeComplex_co,
+    _ArrayLikeNumber_co as _ArrayLikeNumber_co,
+    _ArrayLikeTD64_co as _ArrayLikeTD64_co,
+    _ArrayLikeDT64_co as _ArrayLikeDT64_co,
+    _ArrayLikeObject_co as _ArrayLikeObject_co,
+    _ArrayLikeVoid_co as _ArrayLikeVoid_co,
+    _ArrayLikeStr_co as _ArrayLikeStr_co,
+    _ArrayLikeBytes_co as _ArrayLikeBytes_co,
+    _ArrayLikeUnknown as _ArrayLikeUnknown,
+    _UnknownType as _UnknownType,
+)
+
+if TYPE_CHECKING:
+    from ._ufunc import (
+        _UFunc_Nin1_Nout1 as _UFunc_Nin1_Nout1,
+        _UFunc_Nin2_Nout1 as _UFunc_Nin2_Nout1,
+        _UFunc_Nin1_Nout2 as _UFunc_Nin1_Nout2,
+        _UFunc_Nin2_Nout2 as _UFunc_Nin2_Nout2,
+        _GUFunc_Nin2_Nout1 as _GUFunc_Nin2_Nout1,
+    )
+else:
+    # Declare the (type-check-only) ufunc subclasses as ufunc aliases during
+    # runtime; this helps autocompletion tools such as Jedi (numpy/numpy#19834)
+    _UFunc_Nin1_Nout1 = ufunc
+    _UFunc_Nin2_Nout1 = ufunc
+    _UFunc_Nin1_Nout2 = ufunc
+    _UFunc_Nin2_Nout2 = ufunc
+    _GUFunc_Nin2_Nout1 = ufunc
diff --git a/.venv/lib/python3.12/site-packages/numpy/_typing/_add_docstring.py b/.venv/lib/python3.12/site-packages/numpy/_typing/_add_docstring.py
new file mode 100644
index 00000000..f84d1927
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/_typing/_add_docstring.py
@@ -0,0 +1,152 @@
+"""A module for creating docstrings for sphinx ``data`` domains."""
+
+import re
+import textwrap
+
+from ._array_like import NDArray
+
+_docstrings_list = []
+
+
+def add_newdoc(name: str, value: str, doc: str) -> None:
+    """Append ``_docstrings_list`` with a docstring for `name`.
+
+    Parameters
+    ----------
+    name : str
+        The name of the object.
+    value : str
+        A string-representation of the object.
+    doc : str
+        The docstring of the object.
+
+    """
+    _docstrings_list.append((name, value, doc))
+
+
+def _parse_docstrings() -> str:
+    """Convert all docstrings in ``_docstrings_list`` into a single
+    sphinx-legible text block.
+
+    """
+    type_list_ret = []
+    for name, value, doc in _docstrings_list:
+        s = textwrap.dedent(doc).replace("\n", "\n    ")
+
+        # Replace sections by rubrics
+        lines = s.split("\n")
+        new_lines = []
+        indent = ""
+        for line in lines:
+            m = re.match(r'^(\s+)[-=]+\s*$', line)
+            if m and new_lines:
+                prev = textwrap.dedent(new_lines.pop())
+                if prev == "Examples":
+                    indent = ""
+                    new_lines.append(f'{m.group(1)}.. rubric:: {prev}')
+                else:
+                    indent = 4 * " "
+                    new_lines.append(f'{m.group(1)}.. admonition:: {prev}')
+                new_lines.append("")
+            else:
+                new_lines.append(f"{indent}{line}")
+
+        s = "\n".join(new_lines)
+        s_block = f""".. data:: {name}\n    :value: {value}\n    {s}"""
+        type_list_ret.append(s_block)
+    return "\n".join(type_list_ret)
+
+
+add_newdoc('ArrayLike', 'typing.Union[...]',
+    """
+    A `~typing.Union` representing objects that can be coerced
+    into an `~numpy.ndarray`.
+
+    Among others this includes the likes of:
+
+    * Scalars.
+    * (Nested) sequences.
+    * Objects implementing the `~class.__array__` protocol.
+
+    .. versionadded:: 1.20
+
+    See Also
+    --------
+    :term:`array_like`:
+        Any scalar or sequence that can be interpreted as an ndarray.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        >>> import numpy as np
+        >>> import numpy.typing as npt
+
+        >>> def as_array(a: npt.ArrayLike) -> np.ndarray:
+        ...     return np.array(a)
+
+    """)
+
+add_newdoc('DTypeLike', 'typing.Union[...]',
+    """
+    A `~typing.Union` representing objects that can be coerced
+    into a `~numpy.dtype`.
+
+    Among others this includes the likes of:
+
+    * :class:`type` objects.
+    * Character codes or the names of :class:`type` objects.
+    * Objects with the ``.dtype`` attribute.
+
+    .. versionadded:: 1.20
+
+    See Also
+    --------
+    :ref:`Specifying and constructing data types <arrays.dtypes.constructing>`
+        A comprehensive overview of all objects that can be coerced
+        into data types.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        >>> import numpy as np
+        >>> import numpy.typing as npt
+
+        >>> def as_dtype(d: npt.DTypeLike) -> np.dtype:
+        ...     return np.dtype(d)
+
+    """)
+
+add_newdoc('NDArray', repr(NDArray),
+    """
+    A :term:`generic <generic type>` version of
+    `np.ndarray[Any, np.dtype[+ScalarType]] <numpy.ndarray>`.
+
+    Can be used during runtime for typing arrays with a given dtype
+    and unspecified shape.
+
+    .. versionadded:: 1.21
+
+    Examples
+    --------
+    .. code-block:: python
+
+        >>> import numpy as np
+        >>> import numpy.typing as npt
+
+        >>> print(npt.NDArray)
+        numpy.ndarray[typing.Any, numpy.dtype[+ScalarType]]
+
+        >>> print(npt.NDArray[np.float64])
+        numpy.ndarray[typing.Any, numpy.dtype[numpy.float64]]
+
+        >>> NDArrayInt = npt.NDArray[np.int_]
+        >>> a: NDArrayInt = np.arange(10)
+
+        >>> def func(a: npt.ArrayLike) -> npt.NDArray[Any]:
+        ...     return np.array(a)
+
+    """)
+
+_docstrings = _parse_docstrings()
diff --git a/.venv/lib/python3.12/site-packages/numpy/_typing/_array_like.py b/.venv/lib/python3.12/site-packages/numpy/_typing/_array_like.py
new file mode 100644
index 00000000..883e817d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/_typing/_array_like.py
@@ -0,0 +1,167 @@
+from __future__ import annotations
+
+import sys
+from collections.abc import Collection, Callable, Sequence
+from typing import Any, Protocol, Union, TypeVar, runtime_checkable
+
+from numpy import (
+    ndarray,
+    dtype,
+    generic,
+    bool_,
+    unsignedinteger,
+    integer,
+    floating,
+    complexfloating,
+    number,
+    timedelta64,
+    datetime64,
+    object_,
+    void,
+    str_,
+    bytes_,
+)
+from ._nested_sequence import _NestedSequence
+
+_T = TypeVar("_T")
+_ScalarType = TypeVar("_ScalarType", bound=generic)
+_ScalarType_co = TypeVar("_ScalarType_co", bound=generic, covariant=True)
+_DType = TypeVar("_DType", bound=dtype[Any])
+_DType_co = TypeVar("_DType_co", covariant=True, bound=dtype[Any])
+
+NDArray = ndarray[Any, dtype[_ScalarType_co]]
+
+# The `_SupportsArray` protocol only cares about the default dtype
+# (i.e. `dtype=None` or no `dtype` parameter at all) of the to-be returned
+# array.
+# Concrete implementations of the protocol are responsible for adding
+# any and all remaining overloads
+@runtime_checkable
+class _SupportsArray(Protocol[_DType_co]):
+    def __array__(self) -> ndarray[Any, _DType_co]: ...
+
+
+@runtime_checkable
+class _SupportsArrayFunc(Protocol):
+    """A protocol class representing `~class.__array_function__`."""
+    def __array_function__(
+        self,
+        func: Callable[..., Any],
+        types: Collection[type[Any]],
+        args: tuple[Any, ...],
+        kwargs: dict[str, Any],
+    ) -> object: ...
+
+
+# TODO: Wait until mypy supports recursive objects in combination with typevars
+_FiniteNestedSequence = Union[
+    _T,
+    Sequence[_T],
+    Sequence[Sequence[_T]],
+    Sequence[Sequence[Sequence[_T]]],
+    Sequence[Sequence[Sequence[Sequence[_T]]]],
+]
+
+# A subset of `npt.ArrayLike` that can be parametrized w.r.t. `np.generic`
+_ArrayLike = Union[
+    _SupportsArray[dtype[_ScalarType]],
+    _NestedSequence[_SupportsArray[dtype[_ScalarType]]],
+]
+
+# A union representing array-like objects; consists of two typevars:
+# One representing types that can be parametrized w.r.t. `np.dtype`
+# and another one for the rest
+_DualArrayLike = Union[
+    _SupportsArray[_DType],
+    _NestedSequence[_SupportsArray[_DType]],
+    _T,
+    _NestedSequence[_T],
+]
+
+if sys.version_info >= (3, 12):
+    from collections.abc import Buffer
+
+    ArrayLike = Buffer | _DualArrayLike[
+        dtype[Any],
+        Union[bool, int, float, complex, str, bytes],
+    ]
+else:
+    ArrayLike = _DualArrayLike[
+        dtype[Any],
+        Union[bool, int, float, complex, str, bytes],
+    ]
+
+# `ArrayLike<X>_co`: array-like objects that can be coerced into `X`
+# given the casting rules `same_kind`
+_ArrayLikeBool_co = _DualArrayLike[
+    dtype[bool_],
+    bool,
+]
+_ArrayLikeUInt_co = _DualArrayLike[
+    dtype[Union[bool_, unsignedinteger[Any]]],
+    bool,
+]
+_ArrayLikeInt_co = _DualArrayLike[
+    dtype[Union[bool_, integer[Any]]],
+    Union[bool, int],
+]
+_ArrayLikeFloat_co = _DualArrayLike[
+    dtype[Union[bool_, integer[Any], floating[Any]]],
+    Union[bool, int, float],
+]
+_ArrayLikeComplex_co = _DualArrayLike[
+    dtype[Union[
+        bool_,
+        integer[Any],
+        floating[Any],
+        complexfloating[Any, Any],
+    ]],
+    Union[bool, int, float, complex],
+]
+_ArrayLikeNumber_co = _DualArrayLike[
+    dtype[Union[bool_, number[Any]]],
+    Union[bool, int, float, complex],
+]
+_ArrayLikeTD64_co = _DualArrayLike[
+    dtype[Union[bool_, integer[Any], timedelta64]],
+    Union[bool, int],
+]
+_ArrayLikeDT64_co = Union[
+    _SupportsArray[dtype[datetime64]],
+    _NestedSequence[_SupportsArray[dtype[datetime64]]],
+]
+_ArrayLikeObject_co = Union[
+    _SupportsArray[dtype[object_]],
+    _NestedSequence[_SupportsArray[dtype[object_]]],
+]
+
+_ArrayLikeVoid_co = Union[
+    _SupportsArray[dtype[void]],
+    _NestedSequence[_SupportsArray[dtype[void]]],
+]
+_ArrayLikeStr_co = _DualArrayLike[
+    dtype[str_],
+    str,
+]
+_ArrayLikeBytes_co = _DualArrayLike[
+    dtype[bytes_],
+    bytes,
+]
+
+_ArrayLikeInt = _DualArrayLike[
+    dtype[integer[Any]],
+    int,
+]
+
+# Extra ArrayLike type so that pyright can deal with NDArray[Any]
+# Used as the first overload, should only match NDArray[Any],
+# not any actual types.
+# https://github.com/numpy/numpy/pull/22193
+class _UnknownType:
+    ...
+
+
+_ArrayLikeUnknown = _DualArrayLike[
+    dtype[_UnknownType],
+    _UnknownType,
+]
diff --git a/.venv/lib/python3.12/site-packages/numpy/_typing/_callable.pyi b/.venv/lib/python3.12/site-packages/numpy/_typing/_callable.pyi
new file mode 100644
index 00000000..ee818e90
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/_typing/_callable.pyi
@@ -0,0 +1,338 @@
+"""
+A module with various ``typing.Protocol`` subclasses that implement
+the ``__call__`` magic method.
+
+See the `Mypy documentation`_ on protocols for more details.
+
+.. _`Mypy documentation`: https://mypy.readthedocs.io/en/stable/protocols.html#callback-protocols
+
+"""
+
+from __future__ import annotations
+
+from typing import (
+    TypeVar,
+    overload,
+    Any,
+    NoReturn,
+    Protocol,
+)
+
+from numpy import (
+    ndarray,
+    dtype,
+    generic,
+    bool_,
+    timedelta64,
+    number,
+    integer,
+    unsignedinteger,
+    signedinteger,
+    int8,
+    int_,
+    floating,
+    float64,
+    complexfloating,
+    complex128,
+)
+from ._nbit import _NBitInt, _NBitDouble
+from ._scalars import (
+    _BoolLike_co,
+    _IntLike_co,
+    _FloatLike_co,
+    _NumberLike_co,
+)
+from . import NBitBase
+from ._array_like import NDArray
+from ._nested_sequence import _NestedSequence
+
+_T1 = TypeVar("_T1")
+_T2 = TypeVar("_T2")
+_T1_contra = TypeVar("_T1_contra", contravariant=True)
+_T2_contra = TypeVar("_T2_contra", contravariant=True)
+_2Tuple = tuple[_T1, _T1]
+
+_NBit1 = TypeVar("_NBit1", bound=NBitBase)
+_NBit2 = TypeVar("_NBit2", bound=NBitBase)
+
+_IntType = TypeVar("_IntType", bound=integer)
+_FloatType = TypeVar("_FloatType", bound=floating)
+_NumberType = TypeVar("_NumberType", bound=number)
+_NumberType_co = TypeVar("_NumberType_co", covariant=True, bound=number)
+_GenericType_co = TypeVar("_GenericType_co", covariant=True, bound=generic)
+
+class _BoolOp(Protocol[_GenericType_co]):
+    @overload
+    def __call__(self, other: _BoolLike_co, /) -> _GenericType_co: ...
+    @overload  # platform dependent
+    def __call__(self, other: int, /) -> int_: ...
+    @overload
+    def __call__(self, other: float, /) -> float64: ...
+    @overload
+    def __call__(self, other: complex, /) -> complex128: ...
+    @overload
+    def __call__(self, other: _NumberType, /) -> _NumberType: ...
+
+class _BoolBitOp(Protocol[_GenericType_co]):
+    @overload
+    def __call__(self, other: _BoolLike_co, /) -> _GenericType_co: ...
+    @overload  # platform dependent
+    def __call__(self, other: int, /) -> int_: ...
+    @overload
+    def __call__(self, other: _IntType, /) -> _IntType: ...
+
+class _BoolSub(Protocol):
+    # Note that `other: bool_` is absent here
+    @overload
+    def __call__(self, other: bool, /) -> NoReturn: ...
+    @overload  # platform dependent
+    def __call__(self, other: int, /) -> int_: ...
+    @overload
+    def __call__(self, other: float, /) -> float64: ...
+    @overload
+    def __call__(self, other: complex, /) -> complex128: ...
+    @overload
+    def __call__(self, other: _NumberType, /) -> _NumberType: ...
+
+class _BoolTrueDiv(Protocol):
+    @overload
+    def __call__(self, other: float | _IntLike_co, /) -> float64: ...
+    @overload
+    def __call__(self, other: complex, /) -> complex128: ...
+    @overload
+    def __call__(self, other: _NumberType, /) -> _NumberType: ...
+
+class _BoolMod(Protocol):
+    @overload
+    def __call__(self, other: _BoolLike_co, /) -> int8: ...
+    @overload  # platform dependent
+    def __call__(self, other: int, /) -> int_: ...
+    @overload
+    def __call__(self, other: float, /) -> float64: ...
+    @overload
+    def __call__(self, other: _IntType, /) -> _IntType: ...
+    @overload
+    def __call__(self, other: _FloatType, /) -> _FloatType: ...
+
+class _BoolDivMod(Protocol):
+    @overload
+    def __call__(self, other: _BoolLike_co, /) -> _2Tuple[int8]: ...
+    @overload  # platform dependent
+    def __call__(self, other: int, /) -> _2Tuple[int_]: ...
+    @overload
+    def __call__(self, other: float, /) -> _2Tuple[floating[_NBit1 | _NBitDouble]]: ...
+    @overload
+    def __call__(self, other: _IntType, /) -> _2Tuple[_IntType]: ...
+    @overload
+    def __call__(self, other: _FloatType, /) -> _2Tuple[_FloatType]: ...
+
+class _TD64Div(Protocol[_NumberType_co]):
+    @overload
+    def __call__(self, other: timedelta64, /) -> _NumberType_co: ...
+    @overload
+    def __call__(self, other: _BoolLike_co, /) -> NoReturn: ...
+    @overload
+    def __call__(self, other: _FloatLike_co, /) -> timedelta64: ...
+
+class _IntTrueDiv(Protocol[_NBit1]):
+    @overload
+    def __call__(self, other: bool, /) -> floating[_NBit1]: ...
+    @overload
+    def __call__(self, other: int, /) -> floating[_NBit1 | _NBitInt]: ...
+    @overload
+    def __call__(self, other: float, /) -> floating[_NBit1 | _NBitDouble]: ...
+    @overload
+    def __call__(
+        self, other: complex, /,
+    ) -> complexfloating[_NBit1 | _NBitDouble, _NBit1 | _NBitDouble]: ...
+    @overload
+    def __call__(self, other: integer[_NBit2], /) -> floating[_NBit1 | _NBit2]: ...
+
+class _UnsignedIntOp(Protocol[_NBit1]):
+    # NOTE: `uint64 + signedinteger -> float64`
+    @overload
+    def __call__(self, other: bool, /) -> unsignedinteger[_NBit1]: ...
+    @overload
+    def __call__(
+        self, other: int | signedinteger[Any], /
+    ) -> Any: ...
+    @overload
+    def __call__(self, other: float, /) -> floating[_NBit1 | _NBitDouble]: ...
+    @overload
+    def __call__(
+        self, other: complex, /,
+    ) -> complexfloating[_NBit1 | _NBitDouble, _NBit1 | _NBitDouble]: ...
+    @overload
+    def __call__(
+        self, other: unsignedinteger[_NBit2], /
+    ) -> unsignedinteger[_NBit1 | _NBit2]: ...
+
+class _UnsignedIntBitOp(Protocol[_NBit1]):
+    @overload
+    def __call__(self, other: bool, /) -> unsignedinteger[_NBit1]: ...
+    @overload
+    def __call__(self, other: int, /) -> signedinteger[Any]: ...
+    @overload
+    def __call__(self, other: signedinteger[Any], /) -> signedinteger[Any]: ...
+    @overload
+    def __call__(
+        self, other: unsignedinteger[_NBit2], /
+    ) -> unsignedinteger[_NBit1 | _NBit2]: ...
+
+class _UnsignedIntMod(Protocol[_NBit1]):
+    @overload
+    def __call__(self, other: bool, /) -> unsignedinteger[_NBit1]: ...
+    @overload
+    def __call__(
+        self, other: int | signedinteger[Any], /
+    ) -> Any: ...
+    @overload
+    def __call__(self, other: float, /) -> floating[_NBit1 | _NBitDouble]: ...
+    @overload
+    def __call__(
+        self, other: unsignedinteger[_NBit2], /
+    ) -> unsignedinteger[_NBit1 | _NBit2]: ...
+
+class _UnsignedIntDivMod(Protocol[_NBit1]):
+    @overload
+    def __call__(self, other: bool, /) -> _2Tuple[signedinteger[_NBit1]]: ...
+    @overload
+    def __call__(
+        self, other: int | signedinteger[Any], /
+    ) -> _2Tuple[Any]: ...
+    @overload
+    def __call__(self, other: float, /) -> _2Tuple[floating[_NBit1 | _NBitDouble]]: ...
+    @overload
+    def __call__(
+        self, other: unsignedinteger[_NBit2], /
+    ) -> _2Tuple[unsignedinteger[_NBit1 | _NBit2]]: ...
+
+class _SignedIntOp(Protocol[_NBit1]):
+    @overload
+    def __call__(self, other: bool, /) -> signedinteger[_NBit1]: ...
+    @overload
+    def __call__(self, other: int, /) -> signedinteger[_NBit1 | _NBitInt]: ...
+    @overload
+    def __call__(self, other: float, /) -> floating[_NBit1 | _NBitDouble]: ...
+    @overload
+    def __call__(
+        self, other: complex, /,
+    ) -> complexfloating[_NBit1 | _NBitDouble, _NBit1 | _NBitDouble]: ...
+    @overload
+    def __call__(
+        self, other: signedinteger[_NBit2], /,
+    ) -> signedinteger[_NBit1 | _NBit2]: ...
+
+class _SignedIntBitOp(Protocol[_NBit1]):
+    @overload
+    def __call__(self, other: bool, /) -> signedinteger[_NBit1]: ...
+    @overload
+    def __call__(self, other: int, /) -> signedinteger[_NBit1 | _NBitInt]: ...
+    @overload
+    def __call__(
+        self, other: signedinteger[_NBit2], /,
+    ) -> signedinteger[_NBit1 | _NBit2]: ...
+
+class _SignedIntMod(Protocol[_NBit1]):
+    @overload
+    def __call__(self, other: bool, /) -> signedinteger[_NBit1]: ...
+    @overload
+    def __call__(self, other: int, /) -> signedinteger[_NBit1 | _NBitInt]: ...
+    @overload
+    def __call__(self, other: float, /) -> floating[_NBit1 | _NBitDouble]: ...
+    @overload
+    def __call__(
+        self, other: signedinteger[_NBit2], /,
+    ) -> signedinteger[_NBit1 | _NBit2]: ...
+
+class _SignedIntDivMod(Protocol[_NBit1]):
+    @overload
+    def __call__(self, other: bool, /) -> _2Tuple[signedinteger[_NBit1]]: ...
+    @overload
+    def __call__(self, other: int, /) -> _2Tuple[signedinteger[_NBit1 | _NBitInt]]: ...
+    @overload
+    def __call__(self, other: float, /) -> _2Tuple[floating[_NBit1 | _NBitDouble]]: ...
+    @overload
+    def __call__(
+        self, other: signedinteger[_NBit2], /,
+    ) -> _2Tuple[signedinteger[_NBit1 | _NBit2]]: ...
+
+class _FloatOp(Protocol[_NBit1]):
+    @overload
+    def __call__(self, other: bool, /) -> floating[_NBit1]: ...
+    @overload
+    def __call__(self, other: int, /) -> floating[_NBit1 | _NBitInt]: ...
+    @overload
+    def __call__(self, other: float, /) -> floating[_NBit1 | _NBitDouble]: ...
+    @overload
+    def __call__(
+        self, other: complex, /,
+    ) -> complexfloating[_NBit1 | _NBitDouble, _NBit1 | _NBitDouble]: ...
+    @overload
+    def __call__(
+        self, other: integer[_NBit2] | floating[_NBit2], /
+    ) -> floating[_NBit1 | _NBit2]: ...
+
+class _FloatMod(Protocol[_NBit1]):
+    @overload
+    def __call__(self, other: bool, /) -> floating[_NBit1]: ...
+    @overload
+    def __call__(self, other: int, /) -> floating[_NBit1 | _NBitInt]: ...
+    @overload
+    def __call__(self, other: float, /) -> floating[_NBit1 | _NBitDouble]: ...
+    @overload
+    def __call__(
+        self, other: integer[_NBit2] | floating[_NBit2], /
+    ) -> floating[_NBit1 | _NBit2]: ...
+
+class _FloatDivMod(Protocol[_NBit1]):
+    @overload
+    def __call__(self, other: bool, /) -> _2Tuple[floating[_NBit1]]: ...
+    @overload
+    def __call__(self, other: int, /) -> _2Tuple[floating[_NBit1 | _NBitInt]]: ...
+    @overload
+    def __call__(self, other: float, /) -> _2Tuple[floating[_NBit1 | _NBitDouble]]: ...
+    @overload
+    def __call__(
+        self, other: integer[_NBit2] | floating[_NBit2], /
+    ) -> _2Tuple[floating[_NBit1 | _NBit2]]: ...
+
+class _ComplexOp(Protocol[_NBit1]):
+    @overload
+    def __call__(self, other: bool, /) -> complexfloating[_NBit1, _NBit1]: ...
+    @overload
+    def __call__(self, other: int, /) -> complexfloating[_NBit1 | _NBitInt, _NBit1 | _NBitInt]: ...
+    @overload
+    def __call__(
+        self, other: complex, /,
+    ) -> complexfloating[_NBit1 | _NBitDouble, _NBit1 | _NBitDouble]: ...
+    @overload
+    def __call__(
+        self,
+        other: (
+            integer[_NBit2]
+            | floating[_NBit2]
+            | complexfloating[_NBit2, _NBit2]
+        ), /,
+    ) -> complexfloating[_NBit1 | _NBit2, _NBit1 | _NBit2]: ...
+
+class _NumberOp(Protocol):
+    def __call__(self, other: _NumberLike_co, /) -> Any: ...
+
+class _SupportsLT(Protocol):
+    def __lt__(self, other: Any, /) -> object: ...
+
+class _SupportsGT(Protocol):
+    def __gt__(self, other: Any, /) -> object: ...
+
+class _ComparisonOp(Protocol[_T1_contra, _T2_contra]):
+    @overload
+    def __call__(self, other: _T1_contra, /) -> bool_: ...
+    @overload
+    def __call__(self, other: _T2_contra, /) -> NDArray[bool_]: ...
+    @overload
+    def __call__(
+        self,
+        other: _SupportsLT | _SupportsGT | _NestedSequence[_SupportsLT | _SupportsGT],
+        /,
+    ) -> Any: ...
diff --git a/.venv/lib/python3.12/site-packages/numpy/_typing/_char_codes.py b/.venv/lib/python3.12/site-packages/numpy/_typing/_char_codes.py
new file mode 100644
index 00000000..f840d17b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/_typing/_char_codes.py
@@ -0,0 +1,111 @@
+from typing import Literal
+
+_BoolCodes = Literal["?", "=?", "<?", ">?", "bool", "bool_", "bool8"]
+
+_UInt8Codes = Literal["uint8", "u1", "=u1", "<u1", ">u1"]
+_UInt16Codes = Literal["uint16", "u2", "=u2", "<u2", ">u2"]
+_UInt32Codes = Literal["uint32", "u4", "=u4", "<u4", ">u4"]
+_UInt64Codes = Literal["uint64", "u8", "=u8", "<u8", ">u8"]
+
+_Int8Codes = Literal["int8", "i1", "=i1", "<i1", ">i1"]
+_Int16Codes = Literal["int16", "i2", "=i2", "<i2", ">i2"]
+_Int32Codes = Literal["int32", "i4", "=i4", "<i4", ">i4"]
+_Int64Codes = Literal["int64", "i8", "=i8", "<i8", ">i8"]
+
+_Float16Codes = Literal["float16", "f2", "=f2", "<f2", ">f2"]
+_Float32Codes = Literal["float32", "f4", "=f4", "<f4", ">f4"]
+_Float64Codes = Literal["float64", "f8", "=f8", "<f8", ">f8"]
+
+_Complex64Codes = Literal["complex64", "c8", "=c8", "<c8", ">c8"]
+_Complex128Codes = Literal["complex128", "c16", "=c16", "<c16", ">c16"]
+
+_ByteCodes = Literal["byte", "b", "=b", "<b", ">b"]
+_ShortCodes = Literal["short", "h", "=h", "<h", ">h"]
+_IntCCodes = Literal["intc", "i", "=i", "<i", ">i"]
+_IntPCodes = Literal["intp", "int0", "p", "=p", "<p", ">p"]
+_IntCodes = Literal["long", "int", "int_", "l", "=l", "<l", ">l"]
+_LongLongCodes = Literal["longlong", "q", "=q", "<q", ">q"]
+
+_UByteCodes = Literal["ubyte", "B", "=B", "<B", ">B"]
+_UShortCodes = Literal["ushort", "H", "=H", "<H", ">H"]
+_UIntCCodes = Literal["uintc", "I", "=I", "<I", ">I"]
+_UIntPCodes = Literal["uintp", "uint0", "P", "=P", "<P", ">P"]
+_UIntCodes = Literal["ulong", "uint", "L", "=L", "<L", ">L"]
+_ULongLongCodes = Literal["ulonglong", "Q", "=Q", "<Q", ">Q"]
+
+_HalfCodes = Literal["half", "e", "=e", "<e", ">e"]
+_SingleCodes = Literal["single", "f", "=f", "<f", ">f"]
+_DoubleCodes = Literal["double", "float", "float_", "d", "=d", "<d", ">d"]
+_LongDoubleCodes = Literal["longdouble", "longfloat", "g", "=g", "<g", ">g"]
+
+_CSingleCodes = Literal["csingle", "singlecomplex", "F", "=F", "<F", ">F"]
+_CDoubleCodes = Literal["cdouble", "complex", "complex_", "cfloat", "D", "=D", "<D", ">D"]
+_CLongDoubleCodes = Literal["clongdouble", "clongfloat", "longcomplex", "G", "=G", "<G", ">G"]
+
+_StrCodes = Literal["str", "str_", "str0", "unicode", "unicode_", "U", "=U", "<U", ">U"]
+_BytesCodes = Literal["bytes", "bytes_", "bytes0", "S", "=S", "<S", ">S"]
+_VoidCodes = Literal["void", "void0", "V", "=V", "<V", ">V"]
+_ObjectCodes = Literal["object", "object_", "O", "=O", "<O", ">O"]
+
+_DT64Codes = Literal[
+    "datetime64", "=datetime64", "<datetime64", ">datetime64",
+    "datetime64[Y]", "=datetime64[Y]", "<datetime64[Y]", ">datetime64[Y]",
+    "datetime64[M]", "=datetime64[M]", "<datetime64[M]", ">datetime64[M]",
+    "datetime64[W]", "=datetime64[W]", "<datetime64[W]", ">datetime64[W]",
+    "datetime64[D]", "=datetime64[D]", "<datetime64[D]", ">datetime64[D]",
+    "datetime64[h]", "=datetime64[h]", "<datetime64[h]", ">datetime64[h]",
+    "datetime64[m]", "=datetime64[m]", "<datetime64[m]", ">datetime64[m]",
+    "datetime64[s]", "=datetime64[s]", "<datetime64[s]", ">datetime64[s]",
+    "datetime64[ms]", "=datetime64[ms]", "<datetime64[ms]", ">datetime64[ms]",
+    "datetime64[us]", "=datetime64[us]", "<datetime64[us]", ">datetime64[us]",
+    "datetime64[ns]", "=datetime64[ns]", "<datetime64[ns]", ">datetime64[ns]",
+    "datetime64[ps]", "=datetime64[ps]", "<datetime64[ps]", ">datetime64[ps]",
+    "datetime64[fs]", "=datetime64[fs]", "<datetime64[fs]", ">datetime64[fs]",
+    "datetime64[as]", "=datetime64[as]", "<datetime64[as]", ">datetime64[as]",
+    "M", "=M", "<M", ">M",
+    "M8", "=M8", "<M8", ">M8",
+    "M8[Y]", "=M8[Y]", "<M8[Y]", ">M8[Y]",
+    "M8[M]", "=M8[M]", "<M8[M]", ">M8[M]",
+    "M8[W]", "=M8[W]", "<M8[W]", ">M8[W]",
+    "M8[D]", "=M8[D]", "<M8[D]", ">M8[D]",
+    "M8[h]", "=M8[h]", "<M8[h]", ">M8[h]",
+    "M8[m]", "=M8[m]", "<M8[m]", ">M8[m]",
+    "M8[s]", "=M8[s]", "<M8[s]", ">M8[s]",
+    "M8[ms]", "=M8[ms]", "<M8[ms]", ">M8[ms]",
+    "M8[us]", "=M8[us]", "<M8[us]", ">M8[us]",
+    "M8[ns]", "=M8[ns]", "<M8[ns]", ">M8[ns]",
+    "M8[ps]", "=M8[ps]", "<M8[ps]", ">M8[ps]",
+    "M8[fs]", "=M8[fs]", "<M8[fs]", ">M8[fs]",
+    "M8[as]", "=M8[as]", "<M8[as]", ">M8[as]",
+]
+_TD64Codes = Literal[
+    "timedelta64", "=timedelta64", "<timedelta64", ">timedelta64",
+    "timedelta64[Y]", "=timedelta64[Y]", "<timedelta64[Y]", ">timedelta64[Y]",
+    "timedelta64[M]", "=timedelta64[M]", "<timedelta64[M]", ">timedelta64[M]",
+    "timedelta64[W]", "=timedelta64[W]", "<timedelta64[W]", ">timedelta64[W]",
+    "timedelta64[D]", "=timedelta64[D]", "<timedelta64[D]", ">timedelta64[D]",
+    "timedelta64[h]", "=timedelta64[h]", "<timedelta64[h]", ">timedelta64[h]",
+    "timedelta64[m]", "=timedelta64[m]", "<timedelta64[m]", ">timedelta64[m]",
+    "timedelta64[s]", "=timedelta64[s]", "<timedelta64[s]", ">timedelta64[s]",
+    "timedelta64[ms]", "=timedelta64[ms]", "<timedelta64[ms]", ">timedelta64[ms]",
+    "timedelta64[us]", "=timedelta64[us]", "<timedelta64[us]", ">timedelta64[us]",
+    "timedelta64[ns]", "=timedelta64[ns]", "<timedelta64[ns]", ">timedelta64[ns]",
+    "timedelta64[ps]", "=timedelta64[ps]", "<timedelta64[ps]", ">timedelta64[ps]",
+    "timedelta64[fs]", "=timedelta64[fs]", "<timedelta64[fs]", ">timedelta64[fs]",
+    "timedelta64[as]", "=timedelta64[as]", "<timedelta64[as]", ">timedelta64[as]",
+    "m", "=m", "<m", ">m",
+    "m8", "=m8", "<m8", ">m8",
+    "m8[Y]", "=m8[Y]", "<m8[Y]", ">m8[Y]",
+    "m8[M]", "=m8[M]", "<m8[M]", ">m8[M]",
+    "m8[W]", "=m8[W]", "<m8[W]", ">m8[W]",
+    "m8[D]", "=m8[D]", "<m8[D]", ">m8[D]",
+    "m8[h]", "=m8[h]", "<m8[h]", ">m8[h]",
+    "m8[m]", "=m8[m]", "<m8[m]", ">m8[m]",
+    "m8[s]", "=m8[s]", "<m8[s]", ">m8[s]",
+    "m8[ms]", "=m8[ms]", "<m8[ms]", ">m8[ms]",
+    "m8[us]", "=m8[us]", "<m8[us]", ">m8[us]",
+    "m8[ns]", "=m8[ns]", "<m8[ns]", ">m8[ns]",
+    "m8[ps]", "=m8[ps]", "<m8[ps]", ">m8[ps]",
+    "m8[fs]", "=m8[fs]", "<m8[fs]", ">m8[fs]",
+    "m8[as]", "=m8[as]", "<m8[as]", ">m8[as]",
+]
diff --git a/.venv/lib/python3.12/site-packages/numpy/_typing/_dtype_like.py b/.venv/lib/python3.12/site-packages/numpy/_typing/_dtype_like.py
new file mode 100644
index 00000000..207a99c5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/_typing/_dtype_like.py
@@ -0,0 +1,246 @@
+from collections.abc import Sequence
+from typing import (
+    Any,
+    Sequence,
+    Union,
+    TypeVar,
+    Protocol,
+    TypedDict,
+    runtime_checkable,
+)
+
+import numpy as np
+
+from ._shape import _ShapeLike
+
+from ._char_codes import (
+    _BoolCodes,
+    _UInt8Codes,
+    _UInt16Codes,
+    _UInt32Codes,
+    _UInt64Codes,
+    _Int8Codes,
+    _Int16Codes,
+    _Int32Codes,
+    _Int64Codes,
+    _Float16Codes,
+    _Float32Codes,
+    _Float64Codes,
+    _Complex64Codes,
+    _Complex128Codes,
+    _ByteCodes,
+    _ShortCodes,
+    _IntCCodes,
+    _IntPCodes,
+    _IntCodes,
+    _LongLongCodes,
+    _UByteCodes,
+    _UShortCodes,
+    _UIntCCodes,
+    _UIntPCodes,
+    _UIntCodes,
+    _ULongLongCodes,
+    _HalfCodes,
+    _SingleCodes,
+    _DoubleCodes,
+    _LongDoubleCodes,
+    _CSingleCodes,
+    _CDoubleCodes,
+    _CLongDoubleCodes,
+    _DT64Codes,
+    _TD64Codes,
+    _StrCodes,
+    _BytesCodes,
+    _VoidCodes,
+    _ObjectCodes,
+)
+
+_SCT = TypeVar("_SCT", bound=np.generic)
+_DType_co = TypeVar("_DType_co", covariant=True, bound=np.dtype[Any])
+
+_DTypeLikeNested = Any  # TODO: wait for support for recursive types
+
+
+# Mandatory keys
+class _DTypeDictBase(TypedDict):
+    names: Sequence[str]
+    formats: Sequence[_DTypeLikeNested]
+
+
+# Mandatory + optional keys
+class _DTypeDict(_DTypeDictBase, total=False):
+    # Only `str` elements are usable as indexing aliases,
+    # but `titles` can in principle accept any object
+    offsets: Sequence[int]
+    titles: Sequence[Any]
+    itemsize: int
+    aligned: bool
+
+
+# A protocol for anything with the dtype attribute
+@runtime_checkable
+class _SupportsDType(Protocol[_DType_co]):
+    @property
+    def dtype(self) -> _DType_co: ...
+
+
+# A subset of `npt.DTypeLike` that can be parametrized w.r.t. `np.generic`
+_DTypeLike = Union[
+    np.dtype[_SCT],
+    type[_SCT],
+    _SupportsDType[np.dtype[_SCT]],
+]
+
+
+# Would create a dtype[np.void]
+_VoidDTypeLike = Union[
+    # (flexible_dtype, itemsize)
+    tuple[_DTypeLikeNested, int],
+    # (fixed_dtype, shape)
+    tuple[_DTypeLikeNested, _ShapeLike],
+    # [(field_name, field_dtype, field_shape), ...]
+    #
+    # The type here is quite broad because NumPy accepts quite a wide
+    # range of inputs inside the list; see the tests for some
+    # examples.
+    list[Any],
+    # {'names': ..., 'formats': ..., 'offsets': ..., 'titles': ...,
+    #  'itemsize': ...}
+    _DTypeDict,
+    # (base_dtype, new_dtype)
+    tuple[_DTypeLikeNested, _DTypeLikeNested],
+]
+
+# Anything that can be coerced into numpy.dtype.
+# Reference: https://docs.scipy.org/doc/numpy/reference/arrays.dtypes.html
+DTypeLike = Union[
+    np.dtype[Any],
+    # default data type (float64)
+    None,
+    # array-scalar types and generic types
+    type[Any],  # NOTE: We're stuck with `type[Any]` due to object dtypes
+    # anything with a dtype attribute
+    _SupportsDType[np.dtype[Any]],
+    # character codes, type strings or comma-separated fields, e.g., 'float64'
+    str,
+    _VoidDTypeLike,
+]
+
+# NOTE: while it is possible to provide the dtype as a dict of
+# dtype-like objects (e.g. `{'field1': ..., 'field2': ..., ...}`),
+# this syntax is officially discourged and
+# therefore not included in the Union defining `DTypeLike`.
+#
+# See https://github.com/numpy/numpy/issues/16891 for more details.
+
+# Aliases for commonly used dtype-like objects.
+# Note that the precision of `np.number` subclasses is ignored herein.
+_DTypeLikeBool = Union[
+    type[bool],
+    type[np.bool_],
+    np.dtype[np.bool_],
+    _SupportsDType[np.dtype[np.bool_]],
+    _BoolCodes,
+]
+_DTypeLikeUInt = Union[
+    type[np.unsignedinteger],
+    np.dtype[np.unsignedinteger],
+    _SupportsDType[np.dtype[np.unsignedinteger]],
+    _UInt8Codes,
+    _UInt16Codes,
+    _UInt32Codes,
+    _UInt64Codes,
+    _UByteCodes,
+    _UShortCodes,
+    _UIntCCodes,
+    _UIntPCodes,
+    _UIntCodes,
+    _ULongLongCodes,
+]
+_DTypeLikeInt = Union[
+    type[int],
+    type[np.signedinteger],
+    np.dtype[np.signedinteger],
+    _SupportsDType[np.dtype[np.signedinteger]],
+    _Int8Codes,
+    _Int16Codes,
+    _Int32Codes,
+    _Int64Codes,
+    _ByteCodes,
+    _ShortCodes,
+    _IntCCodes,
+    _IntPCodes,
+    _IntCodes,
+    _LongLongCodes,
+]
+_DTypeLikeFloat = Union[
+    type[float],
+    type[np.floating],
+    np.dtype[np.floating],
+    _SupportsDType[np.dtype[np.floating]],
+    _Float16Codes,
+    _Float32Codes,
+    _Float64Codes,
+    _HalfCodes,
+    _SingleCodes,
+    _DoubleCodes,
+    _LongDoubleCodes,
+]
+_DTypeLikeComplex = Union[
+    type[complex],
+    type[np.complexfloating],
+    np.dtype[np.complexfloating],
+    _SupportsDType[np.dtype[np.complexfloating]],
+    _Complex64Codes,
+    _Complex128Codes,
+    _CSingleCodes,
+    _CDoubleCodes,
+    _CLongDoubleCodes,
+]
+_DTypeLikeDT64 = Union[
+    type[np.timedelta64],
+    np.dtype[np.timedelta64],
+    _SupportsDType[np.dtype[np.timedelta64]],
+    _TD64Codes,
+]
+_DTypeLikeTD64 = Union[
+    type[np.datetime64],
+    np.dtype[np.datetime64],
+    _SupportsDType[np.dtype[np.datetime64]],
+    _DT64Codes,
+]
+_DTypeLikeStr = Union[
+    type[str],
+    type[np.str_],
+    np.dtype[np.str_],
+    _SupportsDType[np.dtype[np.str_]],
+    _StrCodes,
+]
+_DTypeLikeBytes = Union[
+    type[bytes],
+    type[np.bytes_],
+    np.dtype[np.bytes_],
+    _SupportsDType[np.dtype[np.bytes_]],
+    _BytesCodes,
+]
+_DTypeLikeVoid = Union[
+    type[np.void],
+    np.dtype[np.void],
+    _SupportsDType[np.dtype[np.void]],
+    _VoidCodes,
+    _VoidDTypeLike,
+]
+_DTypeLikeObject = Union[
+    type,
+    np.dtype[np.object_],
+    _SupportsDType[np.dtype[np.object_]],
+    _ObjectCodes,
+]
+
+_DTypeLikeComplex_co = Union[
+    _DTypeLikeBool,
+    _DTypeLikeUInt,
+    _DTypeLikeInt,
+    _DTypeLikeFloat,
+    _DTypeLikeComplex,
+]
diff --git a/.venv/lib/python3.12/site-packages/numpy/_typing/_extended_precision.py b/.venv/lib/python3.12/site-packages/numpy/_typing/_extended_precision.py
new file mode 100644
index 00000000..7246b47d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/_typing/_extended_precision.py
@@ -0,0 +1,27 @@
+"""A module with platform-specific extended precision
+`numpy.number` subclasses.
+
+The subclasses are defined here (instead of ``__init__.pyi``) such
+that they can be imported conditionally via the numpy's mypy plugin.
+"""
+
+import numpy as np
+from . import (
+    _80Bit,
+    _96Bit,
+    _128Bit,
+    _256Bit,
+)
+
+uint128 = np.unsignedinteger[_128Bit]
+uint256 = np.unsignedinteger[_256Bit]
+int128 = np.signedinteger[_128Bit]
+int256 = np.signedinteger[_256Bit]
+float80 = np.floating[_80Bit]
+float96 = np.floating[_96Bit]
+float128 = np.floating[_128Bit]
+float256 = np.floating[_256Bit]
+complex160 = np.complexfloating[_80Bit, _80Bit]
+complex192 = np.complexfloating[_96Bit, _96Bit]
+complex256 = np.complexfloating[_128Bit, _128Bit]
+complex512 = np.complexfloating[_256Bit, _256Bit]
diff --git a/.venv/lib/python3.12/site-packages/numpy/_typing/_nbit.py b/.venv/lib/python3.12/site-packages/numpy/_typing/_nbit.py
new file mode 100644
index 00000000..b8d35db4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/_typing/_nbit.py
@@ -0,0 +1,16 @@
+"""A module with the precisions of platform-specific `~numpy.number`s."""
+
+from typing import Any
+
+# To-be replaced with a `npt.NBitBase` subclass by numpy's mypy plugin
+_NBitByte = Any
+_NBitShort = Any
+_NBitIntC = Any
+_NBitIntP = Any
+_NBitInt = Any
+_NBitLongLong = Any
+
+_NBitHalf = Any
+_NBitSingle = Any
+_NBitDouble = Any
+_NBitLongDouble = Any
diff --git a/.venv/lib/python3.12/site-packages/numpy/_typing/_nested_sequence.py b/.venv/lib/python3.12/site-packages/numpy/_typing/_nested_sequence.py
new file mode 100644
index 00000000..3d0d25ae
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/_typing/_nested_sequence.py
@@ -0,0 +1,86 @@
+"""A module containing the `_NestedSequence` protocol."""
+
+from __future__ import annotations
+
+from collections.abc import Iterator
+from typing import (
+    Any,
+    TypeVar,
+    Protocol,
+    runtime_checkable,
+)
+
+__all__ = ["_NestedSequence"]
+
+_T_co = TypeVar("_T_co", covariant=True)
+
+
+@runtime_checkable
+class _NestedSequence(Protocol[_T_co]):
+    """A protocol for representing nested sequences.
+
+    Warning
+    -------
+    `_NestedSequence` currently does not work in combination with typevars,
+    *e.g.* ``def func(a: _NestedSequnce[T]) -> T: ...``.
+
+    See Also
+    --------
+    collections.abc.Sequence
+        ABCs for read-only and mutable :term:`sequences`.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        >>> from __future__ import annotations
+
+        >>> from typing import TYPE_CHECKING
+        >>> import numpy as np
+        >>> from numpy._typing import _NestedSequence
+
+        >>> def get_dtype(seq: _NestedSequence[float]) -> np.dtype[np.float64]:
+        ...     return np.asarray(seq).dtype
+
+        >>> a = get_dtype([1.0])
+        >>> b = get_dtype([[1.0]])
+        >>> c = get_dtype([[[1.0]]])
+        >>> d = get_dtype([[[[1.0]]]])
+
+        >>> if TYPE_CHECKING:
+        ...     reveal_locals()
+        ...     # note: Revealed local types are:
+        ...     # note:     a: numpy.dtype[numpy.floating[numpy._typing._64Bit]]
+        ...     # note:     b: numpy.dtype[numpy.floating[numpy._typing._64Bit]]
+        ...     # note:     c: numpy.dtype[numpy.floating[numpy._typing._64Bit]]
+        ...     # note:     d: numpy.dtype[numpy.floating[numpy._typing._64Bit]]
+
+    """
+
+    def __len__(self, /) -> int:
+        """Implement ``len(self)``."""
+        raise NotImplementedError
+
+    def __getitem__(self, index: int, /) -> _T_co | _NestedSequence[_T_co]:
+        """Implement ``self[x]``."""
+        raise NotImplementedError
+
+    def __contains__(self, x: object, /) -> bool:
+        """Implement ``x in self``."""
+        raise NotImplementedError
+
+    def __iter__(self, /) -> Iterator[_T_co | _NestedSequence[_T_co]]:
+        """Implement ``iter(self)``."""
+        raise NotImplementedError
+
+    def __reversed__(self, /) -> Iterator[_T_co | _NestedSequence[_T_co]]:
+        """Implement ``reversed(self)``."""
+        raise NotImplementedError
+
+    def count(self, value: Any, /) -> int:
+        """Return the number of occurrences of `value`."""
+        raise NotImplementedError
+
+    def index(self, value: Any, /) -> int:
+        """Return the first index of `value`."""
+        raise NotImplementedError
diff --git a/.venv/lib/python3.12/site-packages/numpy/_typing/_scalars.py b/.venv/lib/python3.12/site-packages/numpy/_typing/_scalars.py
new file mode 100644
index 00000000..e46ff04a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/_typing/_scalars.py
@@ -0,0 +1,30 @@
+from typing import Union, Any
+
+import numpy as np
+
+# NOTE: `_StrLike_co` and `_BytesLike_co` are pointless, as `np.str_` and
+# `np.bytes_` are already subclasses of their builtin counterpart
+
+_CharLike_co = Union[str, bytes]
+
+# The 6 `<X>Like_co` type-aliases below represent all scalars that can be
+# coerced into `<X>` (with the casting rule `same_kind`)
+_BoolLike_co = Union[bool, np.bool_]
+_UIntLike_co = Union[_BoolLike_co, np.unsignedinteger[Any]]
+_IntLike_co = Union[_BoolLike_co, int, np.integer[Any]]
+_FloatLike_co = Union[_IntLike_co, float, np.floating[Any]]
+_ComplexLike_co = Union[_FloatLike_co, complex, np.complexfloating[Any, Any]]
+_TD64Like_co = Union[_IntLike_co, np.timedelta64]
+
+_NumberLike_co = Union[int, float, complex, np.number[Any], np.bool_]
+_ScalarLike_co = Union[
+    int,
+    float,
+    complex,
+    str,
+    bytes,
+    np.generic,
+]
+
+# `_VoidLike_co` is technically not a scalar, but it's close enough
+_VoidLike_co = Union[tuple[Any, ...], np.void]
diff --git a/.venv/lib/python3.12/site-packages/numpy/_typing/_shape.py b/.venv/lib/python3.12/site-packages/numpy/_typing/_shape.py
new file mode 100644
index 00000000..4f1204e4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/_typing/_shape.py
@@ -0,0 +1,7 @@
+from collections.abc import Sequence
+from typing import Union, SupportsIndex
+
+_Shape = tuple[int, ...]
+
+# Anything that can be coerced to a shape tuple
+_ShapeLike = Union[SupportsIndex, Sequence[SupportsIndex]]
diff --git a/.venv/lib/python3.12/site-packages/numpy/_typing/_ufunc.pyi b/.venv/lib/python3.12/site-packages/numpy/_typing/_ufunc.pyi
new file mode 100644
index 00000000..9f8e0d4e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/_typing/_ufunc.pyi
@@ -0,0 +1,445 @@
+"""A module with private type-check-only `numpy.ufunc` subclasses.
+
+The signatures of the ufuncs are too varied to reasonably type
+with a single class. So instead, `ufunc` has been expanded into
+four private subclasses, one for each combination of
+`~ufunc.nin` and `~ufunc.nout`.
+
+"""
+
+from typing import (
+    Any,
+    Generic,
+    overload,
+    TypeVar,
+    Literal,
+    SupportsIndex,
+    Protocol,
+)
+
+from numpy import ufunc, _CastingKind, _OrderKACF
+from numpy.typing import NDArray
+
+from ._shape import _ShapeLike
+from ._scalars import _ScalarLike_co
+from ._array_like import ArrayLike, _ArrayLikeBool_co, _ArrayLikeInt_co
+from ._dtype_like import DTypeLike
+
+_T = TypeVar("_T")
+_2Tuple = tuple[_T, _T]
+_3Tuple = tuple[_T, _T, _T]
+_4Tuple = tuple[_T, _T, _T, _T]
+
+_NTypes = TypeVar("_NTypes", bound=int)
+_IDType = TypeVar("_IDType", bound=Any)
+_NameType = TypeVar("_NameType", bound=str)
+
+
+class _SupportsArrayUFunc(Protocol):
+    def __array_ufunc__(
+        self,
+        ufunc: ufunc,
+        method: Literal["__call__", "reduce", "reduceat", "accumulate", "outer", "inner"],
+        *inputs: Any,
+        **kwargs: Any,
+    ) -> Any: ...
+
+
+# NOTE: In reality `extobj` should be a length of list 3 containing an
+# int, an int, and a callable, but there's no way to properly express
+# non-homogenous lists.
+# Use `Any` over `Union` to avoid issues related to lists invariance.
+
+# NOTE: `reduce`, `accumulate`, `reduceat` and `outer` raise a ValueError for
+# ufuncs that don't accept two input arguments and return one output argument.
+# In such cases the respective methods are simply typed as `None`.
+
+# NOTE: Similarly, `at` won't be defined for ufuncs that return
+# multiple outputs; in such cases `at` is typed as `None`
+
+# NOTE: If 2 output types are returned then `out` must be a
+# 2-tuple of arrays. Otherwise `None` or a plain array are also acceptable
+
+class _UFunc_Nin1_Nout1(ufunc, Generic[_NameType, _NTypes, _IDType]):  # type: ignore[misc]
+    @property
+    def __name__(self) -> _NameType: ...
+    @property
+    def ntypes(self) -> _NTypes: ...
+    @property
+    def identity(self) -> _IDType: ...
+    @property
+    def nin(self) -> Literal[1]: ...
+    @property
+    def nout(self) -> Literal[1]: ...
+    @property
+    def nargs(self) -> Literal[2]: ...
+    @property
+    def signature(self) -> None: ...
+    @property
+    def reduce(self) -> None: ...
+    @property
+    def accumulate(self) -> None: ...
+    @property
+    def reduceat(self) -> None: ...
+    @property
+    def outer(self) -> None: ...
+
+    @overload
+    def __call__(
+        self,
+        __x1: _ScalarLike_co,
+        out: None = ...,
+        *,
+        where: None | _ArrayLikeBool_co = ...,
+        casting: _CastingKind = ...,
+        order: _OrderKACF = ...,
+        dtype: DTypeLike = ...,
+        subok: bool = ...,
+        signature: str | _2Tuple[None | str] = ...,
+        extobj: list[Any] = ...,
+    ) -> Any: ...
+    @overload
+    def __call__(
+        self,
+        __x1: ArrayLike,
+        out: None | NDArray[Any] | tuple[NDArray[Any]] = ...,
+        *,
+        where: None | _ArrayLikeBool_co = ...,
+        casting: _CastingKind = ...,
+        order: _OrderKACF = ...,
+        dtype: DTypeLike = ...,
+        subok: bool = ...,
+        signature: str | _2Tuple[None | str] = ...,
+        extobj: list[Any] = ...,
+    ) -> NDArray[Any]: ...
+    @overload
+    def __call__(
+        self,
+        __x1: _SupportsArrayUFunc,
+        out: None | NDArray[Any] | tuple[NDArray[Any]] = ...,
+        *,
+        where: None | _ArrayLikeBool_co = ...,
+        casting: _CastingKind = ...,
+        order: _OrderKACF = ...,
+        dtype: DTypeLike = ...,
+        subok: bool = ...,
+        signature: str | _2Tuple[None | str] = ...,
+        extobj: list[Any] = ...,
+    ) -> Any: ...
+
+    def at(
+        self,
+        a: _SupportsArrayUFunc,
+        indices: _ArrayLikeInt_co,
+        /,
+    ) -> None: ...
+
+class _UFunc_Nin2_Nout1(ufunc, Generic[_NameType, _NTypes, _IDType]):  # type: ignore[misc]
+    @property
+    def __name__(self) -> _NameType: ...
+    @property
+    def ntypes(self) -> _NTypes: ...
+    @property
+    def identity(self) -> _IDType: ...
+    @property
+    def nin(self) -> Literal[2]: ...
+    @property
+    def nout(self) -> Literal[1]: ...
+    @property
+    def nargs(self) -> Literal[3]: ...
+    @property
+    def signature(self) -> None: ...
+
+    @overload
+    def __call__(
+        self,
+        __x1: _ScalarLike_co,
+        __x2: _ScalarLike_co,
+        out: None = ...,
+        *,
+        where: None | _ArrayLikeBool_co = ...,
+        casting: _CastingKind = ...,
+        order: _OrderKACF = ...,
+        dtype: DTypeLike = ...,
+        subok: bool = ...,
+        signature: str | _3Tuple[None | str] = ...,
+        extobj: list[Any] = ...,
+    ) -> Any: ...
+    @overload
+    def __call__(
+        self,
+        __x1: ArrayLike,
+        __x2: ArrayLike,
+        out: None | NDArray[Any] | tuple[NDArray[Any]] = ...,
+        *,
+        where: None | _ArrayLikeBool_co = ...,
+        casting: _CastingKind = ...,
+        order: _OrderKACF = ...,
+        dtype: DTypeLike = ...,
+        subok: bool = ...,
+        signature: str | _3Tuple[None | str] = ...,
+        extobj: list[Any] = ...,
+    ) -> NDArray[Any]: ...
+
+    def at(
+        self,
+        a: NDArray[Any],
+        indices: _ArrayLikeInt_co,
+        b: ArrayLike,
+        /,
+    ) -> None: ...
+
+    def reduce(
+        self,
+        array: ArrayLike,
+        axis: None | _ShapeLike = ...,
+        dtype: DTypeLike = ...,
+        out: None | NDArray[Any] = ...,
+        keepdims: bool = ...,
+        initial: Any = ...,
+        where: _ArrayLikeBool_co = ...,
+    ) -> Any: ...
+
+    def accumulate(
+        self,
+        array: ArrayLike,
+        axis: SupportsIndex = ...,
+        dtype: DTypeLike = ...,
+        out: None | NDArray[Any] = ...,
+    ) -> NDArray[Any]: ...
+
+    def reduceat(
+        self,
+        array: ArrayLike,
+        indices: _ArrayLikeInt_co,
+        axis: SupportsIndex = ...,
+        dtype: DTypeLike = ...,
+        out: None | NDArray[Any] = ...,
+    ) -> NDArray[Any]: ...
+
+    # Expand `**kwargs` into explicit keyword-only arguments
+    @overload
+    def outer(
+        self,
+        A: _ScalarLike_co,
+        B: _ScalarLike_co,
+        /, *,
+        out: None = ...,
+        where: None | _ArrayLikeBool_co = ...,
+        casting: _CastingKind = ...,
+        order: _OrderKACF = ...,
+        dtype: DTypeLike = ...,
+        subok: bool = ...,
+        signature: str | _3Tuple[None | str] = ...,
+        extobj: list[Any] = ...,
+    ) -> Any: ...
+    @overload
+    def outer(  # type: ignore[misc]
+        self,
+        A: ArrayLike,
+        B: ArrayLike,
+        /, *,
+        out: None | NDArray[Any] | tuple[NDArray[Any]] = ...,
+        where: None | _ArrayLikeBool_co = ...,
+        casting: _CastingKind = ...,
+        order: _OrderKACF = ...,
+        dtype: DTypeLike = ...,
+        subok: bool = ...,
+        signature: str | _3Tuple[None | str] = ...,
+        extobj: list[Any] = ...,
+    ) -> NDArray[Any]: ...
+
+class _UFunc_Nin1_Nout2(ufunc, Generic[_NameType, _NTypes, _IDType]):  # type: ignore[misc]
+    @property
+    def __name__(self) -> _NameType: ...
+    @property
+    def ntypes(self) -> _NTypes: ...
+    @property
+    def identity(self) -> _IDType: ...
+    @property
+    def nin(self) -> Literal[1]: ...
+    @property
+    def nout(self) -> Literal[2]: ...
+    @property
+    def nargs(self) -> Literal[3]: ...
+    @property
+    def signature(self) -> None: ...
+    @property
+    def at(self) -> None: ...
+    @property
+    def reduce(self) -> None: ...
+    @property
+    def accumulate(self) -> None: ...
+    @property
+    def reduceat(self) -> None: ...
+    @property
+    def outer(self) -> None: ...
+
+    @overload
+    def __call__(
+        self,
+        __x1: _ScalarLike_co,
+        __out1: None = ...,
+        __out2: None = ...,
+        *,
+        where: None | _ArrayLikeBool_co = ...,
+        casting: _CastingKind = ...,
+        order: _OrderKACF = ...,
+        dtype: DTypeLike = ...,
+        subok: bool = ...,
+        signature: str | _3Tuple[None | str] = ...,
+        extobj: list[Any] = ...,
+    ) -> _2Tuple[Any]: ...
+    @overload
+    def __call__(
+        self,
+        __x1: ArrayLike,
+        __out1: None | NDArray[Any] = ...,
+        __out2: None | NDArray[Any] = ...,
+        *,
+        out: _2Tuple[NDArray[Any]] = ...,
+        where: None | _ArrayLikeBool_co = ...,
+        casting: _CastingKind = ...,
+        order: _OrderKACF = ...,
+        dtype: DTypeLike = ...,
+        subok: bool = ...,
+        signature: str | _3Tuple[None | str] = ...,
+        extobj: list[Any] = ...,
+    ) -> _2Tuple[NDArray[Any]]: ...
+    @overload
+    def __call__(
+        self,
+        __x1: _SupportsArrayUFunc,
+        __out1: None | NDArray[Any] = ...,
+        __out2: None | NDArray[Any] = ...,
+        *,
+        out: _2Tuple[NDArray[Any]] = ...,
+        where: None | _ArrayLikeBool_co = ...,
+        casting: _CastingKind = ...,
+        order: _OrderKACF = ...,
+        dtype: DTypeLike = ...,
+        subok: bool = ...,
+        signature: str | _3Tuple[None | str] = ...,
+        extobj: list[Any] = ...,
+    ) -> _2Tuple[Any]: ...
+
+class _UFunc_Nin2_Nout2(ufunc, Generic[_NameType, _NTypes, _IDType]):  # type: ignore[misc]
+    @property
+    def __name__(self) -> _NameType: ...
+    @property
+    def ntypes(self) -> _NTypes: ...
+    @property
+    def identity(self) -> _IDType: ...
+    @property
+    def nin(self) -> Literal[2]: ...
+    @property
+    def nout(self) -> Literal[2]: ...
+    @property
+    def nargs(self) -> Literal[4]: ...
+    @property
+    def signature(self) -> None: ...
+    @property
+    def at(self) -> None: ...
+    @property
+    def reduce(self) -> None: ...
+    @property
+    def accumulate(self) -> None: ...
+    @property
+    def reduceat(self) -> None: ...
+    @property
+    def outer(self) -> None: ...
+
+    @overload
+    def __call__(
+        self,
+        __x1: _ScalarLike_co,
+        __x2: _ScalarLike_co,
+        __out1: None = ...,
+        __out2: None = ...,
+        *,
+        where: None | _ArrayLikeBool_co = ...,
+        casting: _CastingKind = ...,
+        order: _OrderKACF = ...,
+        dtype: DTypeLike = ...,
+        subok: bool = ...,
+        signature: str | _4Tuple[None | str] = ...,
+        extobj: list[Any] = ...,
+    ) -> _2Tuple[Any]: ...
+    @overload
+    def __call__(
+        self,
+        __x1: ArrayLike,
+        __x2: ArrayLike,
+        __out1: None | NDArray[Any] = ...,
+        __out2: None | NDArray[Any] = ...,
+        *,
+        out: _2Tuple[NDArray[Any]] = ...,
+        where: None | _ArrayLikeBool_co = ...,
+        casting: _CastingKind = ...,
+        order: _OrderKACF = ...,
+        dtype: DTypeLike = ...,
+        subok: bool = ...,
+        signature: str | _4Tuple[None | str] = ...,
+        extobj: list[Any] = ...,
+    ) -> _2Tuple[NDArray[Any]]: ...
+
+class _GUFunc_Nin2_Nout1(ufunc, Generic[_NameType, _NTypes, _IDType]):  # type: ignore[misc]
+    @property
+    def __name__(self) -> _NameType: ...
+    @property
+    def ntypes(self) -> _NTypes: ...
+    @property
+    def identity(self) -> _IDType: ...
+    @property
+    def nin(self) -> Literal[2]: ...
+    @property
+    def nout(self) -> Literal[1]: ...
+    @property
+    def nargs(self) -> Literal[3]: ...
+
+    # NOTE: In practice the only gufunc in the main namespace is `matmul`,
+    # so we can use its signature here
+    @property
+    def signature(self) -> Literal["(n?,k),(k,m?)->(n?,m?)"]: ...
+    @property
+    def reduce(self) -> None: ...
+    @property
+    def accumulate(self) -> None: ...
+    @property
+    def reduceat(self) -> None: ...
+    @property
+    def outer(self) -> None: ...
+    @property
+    def at(self) -> None: ...
+
+    # Scalar for 1D array-likes; ndarray otherwise
+    @overload
+    def __call__(
+        self,
+        __x1: ArrayLike,
+        __x2: ArrayLike,
+        out: None = ...,
+        *,
+        casting: _CastingKind = ...,
+        order: _OrderKACF = ...,
+        dtype: DTypeLike = ...,
+        subok: bool = ...,
+        signature: str | _3Tuple[None | str] = ...,
+        extobj: list[Any] = ...,
+        axes: list[_2Tuple[SupportsIndex]] = ...,
+    ) -> Any: ...
+    @overload
+    def __call__(
+        self,
+        __x1: ArrayLike,
+        __x2: ArrayLike,
+        out: NDArray[Any] | tuple[NDArray[Any]],
+        *,
+        casting: _CastingKind = ...,
+        order: _OrderKACF = ...,
+        dtype: DTypeLike = ...,
+        subok: bool = ...,
+        signature: str | _3Tuple[None | str] = ...,
+        extobj: list[Any] = ...,
+        axes: list[_2Tuple[SupportsIndex]] = ...,
+    ) -> NDArray[Any]: ...
diff --git a/.venv/lib/python3.12/site-packages/numpy/_typing/setup.py b/.venv/lib/python3.12/site-packages/numpy/_typing/setup.py
new file mode 100644
index 00000000..24022fda
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/_typing/setup.py
@@ -0,0 +1,10 @@
+def configuration(parent_package='', top_path=None):
+    from numpy.distutils.misc_util import Configuration
+    config = Configuration('_typing', parent_package, top_path)
+    config.add_data_files('*.pyi')
+    return config
+
+
+if __name__ == '__main__':
+    from numpy.distutils.core import setup
+    setup(configuration=configuration)