diff --git a/docs/apidocs/array.rst b/docs/apidocs/array.rst deleted file mode 100644 index ce8720f6c..000000000 --- a/docs/apidocs/array.rst +++ /dev/null @@ -1,6 +0,0 @@ -.. _qiskit_dynamics-array: - -.. automodule:: qiskit_dynamics.array - :no-members: - :no-inherited-members: - :no-special-members: diff --git a/docs/apidocs/dispatch.rst b/docs/apidocs/dispatch.rst deleted file mode 100644 index 6b8e1e4cf..000000000 --- a/docs/apidocs/dispatch.rst +++ /dev/null @@ -1,6 +0,0 @@ -.. _qiskit_dynamics-dispatch: - -.. automodule:: qiskit_dynamics.dispatch - :no-members: - :no-inherited-members: - :no-special-members: diff --git a/docs/apidocs/index.rst b/docs/apidocs/index.rst index e203f80c7..a755e08e2 100644 --- a/docs/apidocs/index.rst +++ b/docs/apidocs/index.rst @@ -16,5 +16,3 @@ Qiskit Dynamics API Reference pulse perturbation arraylias - array - dispatch diff --git a/qiskit_dynamics/__init__.py b/qiskit_dynamics/__init__.py index b707d440d..5eb49f98c 100644 --- a/qiskit_dynamics/__init__.py +++ b/qiskit_dynamics/__init__.py @@ -45,5 +45,3 @@ from . import signals from . import pulse from . import backend -from . import dispatch -from . import array diff --git a/qiskit_dynamics/array/__init__.py b/qiskit_dynamics/array/__init__.py deleted file mode 100644 index ddab9a769..000000000 --- a/qiskit_dynamics/array/__init__.py +++ /dev/null @@ -1,237 +0,0 @@ -# -*- coding: utf-8 -*- - -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -=========================================== -Array Module (:mod:`qiskit_dynamics.array`) -=========================================== - -.. currentmodule:: qiskit_dynamics.array - - -.. warning:: - - The ``array`` and ``dispatch`` submodules of Qiskit Dynamics have been deprecated as of version - 0.5.0. The use of the ``Array`` class is no longer required to work with different array - libraries in Qiskit Dynamics, and is broken in some cases. Refer to the :ref:`user guide entry - on using different array libraries with Qiskit Dynamics `. - Users can now work directly with the supported array type of their choice, without the need to - wrap them to enable dispatching. The ``array`` and ``dispatch`` submodules will be removed in - version 0.6.0. - -This module contains an :class:`Array` class that wraps N-dimensional array objects from different -libraries. It enables working with different array libraries through a common NumPy-based interface, -along with other functionality for writing array-library agnostic code. - - -Array Class -=========== - -The :class:`Array` class provides a NumPy compatible wrapper to supported Python array libraries. -NumPy functions applied to an :class:`Array` class instance will be dispatched to the corresponding -function for the current Array backend, if a compatible function has been specified in the -:mod:`qiskit_dynamics.dispatch` system. - -The following array libraries have built in support for the -:mod:`qiskit_dynamics.dispatch` module: - -* `NumPy `_ -* `JAX `_ - -Basic Usage ------------ - -.. jupyter-execute:: - :hide-code: - - # suppress deprecation warnings - import warnings - warnings.simplefilter('ignore', category=DeprecationWarning) - -When using the default ``numpy`` backend :class:`Array`, objects can be used interchangably with -``numpy.ndarray``. When ``numpy`` functions are applied to an :class:`Array` object the return type -will be an :class:`Array` instead of an ``numpy.ndarray``. - -.. jupyter-execute:: - - from qiskit_dynamics.array import Array - import numpy as np - - # Initialize an Array - a = Array(np.arange(10)) - - # Apply numpy ufuncs - np.cos(a) + 1j * np.sin(a) - -For the JAX Array backend, only Numpy functions that have a corresponding function in the ``jax`` -library that have been registered with the dispatch module can be applied to the functions. Trying -to apply an unsupported ``numpy`` function to these arrays will raise an exception. - -Default Backend ---------------- - -When initializing a new :class:`Array`, the ``backend`` kwarg is used to specify which array backend -to use. This will convert the input data to an array of this backend if required. If -``backend=None``, the default backend will be used. The initial default backend is always set to -``"numpy"``. - -The current default backend can be viewed by using the class method :meth:`Array.default_backend`. A -different default backend can be set for all :meth:`Array` instances by using the -:meth:`Array.set_default_backend` class method. - -Attributes and Methods ----------------------- - -The :class:`Array` class exposes the same methods and attributes of the wrapped array class and adds -two additional attributes: - -* :attr:`~Array.data` which returns the wrapped array object -* :attr:`~Array.backend` which returns the backend string of the wrapped array. - -All other attributes and methods of the wrapped array are accessible through this class, but with -any array return types wrapped into :class:`Array` objects. For example: - -.. jupyter-execute:: - - # Call an numpy.ndarray method - a.reshape((2, 5)) - - -Array Initialization --------------------- - -An :class:`Array` object can be initialized as ``Array(data)`` from any ``data`` that is: - -1. An ``array`` object of one of the supported backends. -2. An object that can be used to initialize the backend array. -3. An object of a class that defines a ``__qiskit_array__`` method with the - following signature: - - .. code-block:: python - - def __qiskit_array__(self, - dtype: Optional[any] = None, - backend: Optional[str]=None) -> Array: - # conversion from class to Array - -The :meth:`Array.__init__` method has optional kwargs: - -* ``dtype`` (``any``): Equivalent to the ``numpy.array`` ``dtype`` kwarg. For other array backends, - the specified ``dtype`` must be supported. If not specified, the ``dtype`` will - be inferred from the input data. -* ``order`` (``str``): Equivalent to the ``numpy.array`` ``order`` kwarg. For other array - backends, the specified order value must be supported. -* ``backend`` (``str``): The array backend to use. If not specified this will be inferred from the - input data type if it is a backend array instance, otherwise it will be the - :func:`default_backend`. - - -Using Arrays with other Libraries -================================= - -The :class:`Array` class is intended to be used with array functions in -Python libraries that work with any of the supported Array backends. - -Wrapping Functions ------------------- - -Functions from other libraries can be wrapped to work with Array objects using -the :func:`wrap` function as - -.. code-block:: python - - from qiskit_dynamics.array import Array, wrap - - # Some 3rd-party library - import library - - # Wrap library function f - f = wrap(library.f) - - # Apply f to an Array - a = Array([0, 1, 2, 4]) - b = f(a) - -The wrapped function will automatically unwrap any :class:`Array` args and kwargs -for calling the wrapped function. If the result of the function is a backend array -or an instance of a class that defines a ``__qiskit_array__`` method, the wrapped -function will return this as an Array object. Similarly, if the result is a tuple -this conversion will be applied to each element of the tuple where appropriate. - -Wrapping Decorators -------------------- - -The :func:`wrap` function can also be used to wrap function decorators by setting -the ``decorator=True`` kwarg. - -For example, to wrap autograd and jit decorators from the JAX library to work with -Array functions, we can do the following - -.. code-block:: python - - import jax - - jit = wrap(jax.jit, decorator=True) - grad = wrap(jax.grad, decorator=True) - value_and_grad = wrap(jax.value_and_grad, decorator=True) - - f = jit(obj) - g = grad(obj) - h = value_and_grad(obj) - -.. note: - - Setting ``decorator=True`` when calling :func:`wrap` requires that the - signature of the function being wrapped is - ``func(f: Callable, ...) -> Callable``. Using it is equivalent to nested - wrapping - - .. code-block:: python - - f_wrapped = wrap(func, decorator=True)(f) - - is equivalent to - - .. code-block:: python - - f_wrapped = wrap(wrap(func)(f)) - - -Array Class -=========== - -.. autosummary:: - :toctree: ../stubs/ - - Array - -Array Functions -=============== - -.. autosummary:: - :toctree: ../stubs/ - - wrap -""" - - -# Import Array -from .array import Array - -# Import wrapper function -from .wrap import wrap - -# Monkey patch quantum info -# pylint: disable= wrong-import-position -from .patch_qi import * diff --git a/qiskit_dynamics/array/array.py b/qiskit_dynamics/array/array.py deleted file mode 100644 index 9571d2511..000000000 --- a/qiskit_dynamics/array/array.py +++ /dev/null @@ -1,328 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -# pylint: disable=invalid-name,global-statement - -"""Array Class""" - -import warnings -import copy -from functools import wraps -from types import BuiltinMethodType, MethodType -from typing import Any, Optional, Union, Tuple, Set -from numbers import Number - -import numpy -from numpy.lib.mixins import NDArrayOperatorsMixin - -from qiskit_dynamics.dispatch.dispatch import Dispatch, asarray - -__all__ = ["Array"] - -_deprecation_raised = False - - -def _array_deprecation_warn(): - global _deprecation_raised - if not _deprecation_raised: - warnings.warn( - """The array and dispatch submodules of Qiskit Dynamics have been deprecated as of - version 0.5.0. The use of the Array class is no longer required to work with different - array libraries in Qiskit Dynamics, and is broken in some cases. Refer to the user guide - entry on using different array libraries with Qiskit Dynamics. Users can now work - directly with the supported array type of their choice, without the need to wrap them to - enable dispatching. The array and dispatch submodules will be removed in version 0.6.0. - """, - DeprecationWarning, - stacklevel=3, - ) - _deprecation_raised = True - - -class Array(NDArrayOperatorsMixin): - """Qiskit array class. This class is deprecated as of version 0.5.0 and will be removed in - version 0.6.0. - - This class provides a Numpy compatible wrapper to supported Python array libraries. Supported - backends are ``"numpy"`` and ``"jax"``. - """ - - def __init__( - self, - data: Any, - dtype: Optional[Any] = None, - order: Optional[str] = None, - backend: Optional[str] = None, - ): - """Initialize an :class:`Array` container. - - Args: - data: An ``array_like`` input. This can be an object of any type supported by the - registered :math:`asarray` method for the specified backend. - dtype: Optional. The dtype of the returned array. This value must be supported by the - specified array backend. - order: Optional. The array order. This value must be supported by the specified array - backend. - backend: A registered array backend name. If ``None`` the default array backend will - be used. - - Raises: - ValueError: If input cannot be converted to an :class:`Array`. - """ - _array_deprecation_warn() - # Check if we can override setattr and - # set _data and _backend directly - if ( - isinstance(data, numpy.ndarray) - and _is_numpy_backend(backend) - and _is_equivalent_numpy_array(data, dtype, order) - ): - self.__dict__["_data"] = data - self.__dict__["_backend"] = "numpy" - return - - if hasattr(data, "__qiskit_array__"): - array = data.__qiskit_array__(dtype=dtype, backend=backend) - if not isinstance(array, Array): - raise ValueError("object __qiskit_array__ method is not producing an Array") - self._data = array._data - self._backend = array._backend - if dtype or order or (backend and backend != self._backend): - if backend is None: - backend = self._backend - else: - self._backend = backend - self._data = asarray(self._data, dtype=dtype, order=order, backend=backend) - return - - # Standard init - self._data = asarray(data, dtype=dtype, order=order, backend=backend) - self._backend = backend if backend else Dispatch.backend(self._data, subclass=True) - - @property - def data(self): - """The wrapped array data object.""" - return self._data - - @data.setter - def data(self, value): - self._data[:] = value - - @property - def backend(self): - """The backend of the wrapped array class.""" - return self._backend - - @backend.setter - def backend(self, value: str): - Dispatch.validate_backend(value) - self._data = asarray(self._data, backend=value) - self._backend = value - - @classmethod - def set_default_backend(cls, backend: Union[str, None]): - """Set the default array backend.""" - _array_deprecation_warn() - if backend is not None: - Dispatch.validate_backend(backend) - Dispatch.DEFAULT_BACKEND = backend - - @classmethod - def default_backend(cls) -> str: - """Return the default array backend.""" - _array_deprecation_warn() - return Dispatch.DEFAULT_BACKEND - - @classmethod - def available_backends(cls) -> Set[str]: - """Return a tuple of available array backends.""" - _array_deprecation_warn() - return Dispatch.REGISTERED_BACKENDS - - def __repr__(self): - prefix = "Array(" - if self._backend == Dispatch.DEFAULT_BACKEND: - suffix = ")" - else: - suffix = f"backend='{self._backend}')" - return Dispatch.repr(self.backend)(self._data, prefix=prefix, suffix=suffix) - - def __copy__(self): - """Return a shallow copy referencing the same wrapped data array""" - return Array(self._data, backend=self.backend) - - def __deepcopy__(self, memo=None): - """Return a deep copy with a copy of the wrapped data array""" - return Array(copy.deepcopy(self._data), backend=self.backend) - - def __iter__(self): - return iter(self._data) - - def __getstate__(self): - return {"_data": self._data, "_backend": self._backend} - - def __setstate__(self, state): - self._backend = state["_backend"] - self._data = state["_data"] - - def __getitem__(self, key: str) -> Any: - """Return value from wrapped array.""" - return self._data[key] - - def __setitem__(self, key: str, value: Any): - """Return value of wrapped array.""" - self._data[key] = value - - def __setattr__(self, name: str, value: Any): - """Set attribute of wrapped array.""" - if name in ("_data", "data", "_backend", "backend"): - super().__setattr__(name, value) - else: - setattr(self._data, name, value) - - def __getattr__(self, name: str) -> Any: - """Get attribute of wrapped array and convert to an :class:`Array`.""" - # Call attribute on inner array object - attr = getattr(self._data, name) - - # If attribute is a function wrap the return values - if isinstance(attr, (MethodType, BuiltinMethodType)): - - @wraps(attr) - def wrapped_method(*args, **kwargs): - return self._wrap(attr(*args, **kwargs)) - - return wrapped_method - - # If return object is a backend array wrap result - return self._wrap(attr) - - def __qiskit_array__(self, dtype=None, backend=None): - if (backend and backend != self.backend) or (dtype and dtype != self.data.dtype): - return Array(self.data, dtype=dtype, backend=backend) - return self - - def __array__(self, dtype=None) -> numpy.ndarray: - if isinstance(self._data, numpy.ndarray) and (dtype is None or dtype == self._data.dtype): - return self._data - return numpy.asarray(self._data, dtype=dtype) - - def __len__(self) -> int: - return len(self._data) - - def __str__(self) -> str: - return str(self._data) - - def __bool__(self) -> bool: - return bool(self._data) - - def __int__(self): - """Convert size 1 array to an int.""" - if numpy.size(self) != 1: - raise TypeError("only size-1 Arrays can be converted to Python scalars.") - return int(self._data) - - def __float__(self): - """Convert size 1 array to a float.""" - if numpy.size(self) != 1: - raise TypeError("only size-1 Arrays can be converted to Python scalars.") - return float(self._data) - - def __complex__(self): - """Convert size 1 array to a complex.""" - if numpy.size(self) != 1: - raise TypeError("only size-1 Arrays can be converted to Python scalars.") - return complex(self._data) - - @staticmethod - def _wrap(obj: Union[Any, Tuple[Any]], backend: Optional[str] = None) -> Union[Any, Tuple[Any]]: - """Wrap return array backend objects as :class:`Array` objects.""" - if isinstance(obj, tuple): - return tuple( - Array(x, backend=backend) if isinstance(x, Dispatch.REGISTERED_TYPES) else x - for x in obj - ) - if isinstance(obj, Dispatch.REGISTERED_TYPES): - return Array(obj, backend=backend) - return obj - - @classmethod - def _unwrap(cls, obj): - """Unwrap an Array or list of :class:`Array` objects.""" - if isinstance(obj, Array): - return obj._data - if isinstance(obj, tuple): - return tuple(cls._unwrap(i) for i in obj) - if isinstance(obj, list): - return list(cls._unwrap(i) for i in obj) - return obj - - def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): - """Dispatcher for NumPy ufuncs to support the wrapped array backend.""" - out = kwargs.get("out", tuple()) - - for i in inputs + out: - # Only support operations with instances of REGISTERED_TYPES. - # Use ArrayLike instead of type(self) for isinstance to - # allow subclasses that don't override __array_ufunc__ to - # handle ArrayLike objects. - if not isinstance(i, Dispatch.REGISTERED_TYPES + (Array, Number)): - return NotImplemented - - # Defer to the implementation of the ufunc on unwrapped values. - inputs = self._unwrap(inputs) - if out: - kwargs["out"] = self._unwrap(out) - - # Get implementation for backend - backend = self.backend - dispatch_func = Dispatch.array_ufunc(backend, ufunc, method) - if dispatch_func == NotImplemented: - return NotImplemented - result = dispatch_func(*inputs, **kwargs) - - # Not sure what this case from NumPy docs is? - if method == "at": - return None - - # Wrap array results back into Array objects - return self._wrap(result, backend=self.backend) - - def __array_function__(self, func, types, args, kwargs): - """Dispatcher for NumPy array function to support the wrapped :class:`Array` backend.""" - if not all(issubclass(t, (Array,) + Dispatch.REGISTERED_TYPES) for t in types): - return NotImplemented - - # Unwrap function Array arguments - args = self._unwrap(args) - out = kwargs.get("out", tuple()) - if out: - kwargs["out"] = self._unwrap(out) - - # Get implementation for backend - backend = self.backend - dispatch_func = Dispatch.array_function(backend, func) - if dispatch_func == NotImplemented: - return NotImplemented - result = dispatch_func(*args, **kwargs) - return self._wrap(result, backend=self.backend) - - -def _is_numpy_backend(backend: Optional[str] = None): - return backend == "numpy" or (not backend and Dispatch.DEFAULT_BACKEND == "numpy") - - -def _is_equivalent_numpy_array(data: Any, dtype: Optional[Any] = None, order: Optional[str] = None): - return (not dtype or dtype == data.dtype) and ( - not order - or (order == "C" and data.flags["C_CONTIGUOUS"]) - or (order == "F" and data.flags["F_CONTIGUOUS"]) - ) diff --git a/qiskit_dynamics/array/patch_qi.py b/qiskit_dynamics/array/patch_qi.py deleted file mode 100644 index 477d37c19..000000000 --- a/qiskit_dynamics/array/patch_qi.py +++ /dev/null @@ -1,52 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -"""Monkey patch qiskit.quantum_info to work easier with Array.""" - -from qiskit.quantum_info import ( - Statevector, - DensityMatrix, - Operator, - SuperOp, - Choi, - PTM, - Chi, - Pauli, - Clifford, - SparsePauliOp, -) -from .array import Array - -__all__ = [] - - -def __qiskit_array__(self, dtype=None, backend=None): - """Convert qi operator to an Array.""" - return Array(self.data, dtype=dtype, backend=backend) - - -def __to_matrix_array__(self, dtype=None, backend=None): - """Convert object to array through to_matrix method.""" - return Array(self.to_matrix(), dtype=dtype, backend=backend) - - -# Monkey patch quantum info operators - -Statevector.__qiskit_array__ = __qiskit_array__ -DensityMatrix.__qiskit_array__ = __qiskit_array__ -Operator.__qiskit_array__ = __qiskit_array__ -SuperOp.__qiskit_array__ = __qiskit_array__ -Choi.__qiskit_array__ = __qiskit_array__ -Chi.__qiskit_array__ = __qiskit_array__ -PTM.__qiskit_array__ = __qiskit_array__ -Pauli.__qiskit_array__ = __to_matrix_array__ -Clifford.__qiskit_array__ = __to_matrix_array__ -SparsePauliOp.__qiskit_array__ = __to_matrix_array__ diff --git a/qiskit_dynamics/array/wrap.py b/qiskit_dynamics/array/wrap.py deleted file mode 100644 index 3ccbe7e2c..000000000 --- a/qiskit_dynamics/array/wrap.py +++ /dev/null @@ -1,215 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Functions for working with :class:`Array` dispatch.""" - -import functools -from types import FunctionType -from typing import Callable - -from .array import Array - - -def wrap( - func: Callable, wrap_return: bool = True, wrap_args: bool = True, decorator: bool = False -) -> Callable: - r"""Wrap an array backend function to work with :class:`Array`\s. - - Args: - func: A function to wrap. - wrap_return: If ``True`` convert results that are registered array backend types into - :class:`Array` objects. - wrap_args: If ``True`` also wrap function type args and kwargs of the wrapped function. - decorator: If ``True`` the wrapped decorator function ``func`` will also wrap the - decorated functions. - - Returns: - Callable: The wrapped function. - """ - if decorator: - return _wrap_decorator(func, wrap_return=wrap_return, wrap_args=wrap_args) - else: - return _wrap_function(func, wrap_return=wrap_return, wrap_args=wrap_args) - - -def _wrap_array_function(func: Callable) -> Callable: - r"""Wrap a function to handle :class:`Array`\-like inputs and returns.""" - - @functools.wraps(func) - def wrapped_function(*args, **kwargs): - # Unwrap inputs - args = tuple( - x.__qiskit_array__().data if hasattr(x, "__qiskit_array__") else x for x in args - ) - kwargs = dict( - (key, val.__qiskit_array__().data) if hasattr(val, "__qiskit_array__") else (key, val) - for key, val in kwargs.items() - ) - - # Evaluate function with unwrapped inputs - result = func(*args, **kwargs) - - # Unwrap result - if isinstance(result, tuple): - result = tuple( - x.__qiskit_array__().data if hasattr(x, "__qiskit_array__") else x for x in result - ) - elif hasattr(result, "__qiskit_array__"): - result = result.__qiskit_array__().data - return result - - return wrapped_function - - -def _wrap_args(args): - """Return wrapped args.""" - return tuple(_wrap_array_function(x) if isinstance(x, FunctionType) else x for x in args) - - -def _wrap_kwargs(kwargs): - """Return wrapped kwargs.""" - return dict( - (key, _wrap_array_function(val)) if isinstance(val, FunctionType) else (key, val) - for key, val in kwargs.items() - ) - - -def _wrap_function(func: Callable, wrap_return: bool = True, wrap_args: bool = True) -> Callable: - r"""Wrap an array backend function to work with :class:`Array`\s. - - Args: - func: A function to wrap. - wrap_return: If ``True`` convert results that are registered array backend types into - :class:`Array` objects. - wrap_args: If ``True`` also wrap function type args and kwargs of the wrapped function. - - Returns: - Callable: The wrapped function. - """ - # pylint: disable = function-redefined - if wrap_return and wrap_args: - - @functools.wraps(func) - def wrapped_func(*args, **kwargs): - args = _wrap_args(args) - kwargs = _wrap_kwargs(kwargs) - result = _wrap_array_function(func)(*args, **kwargs) - return Array._wrap(result) - - return wrapped_func - - elif wrap_return and not wrap_args: - - @functools.wraps(func) - def wrapped_func(*args, **kwargs): - result = _wrap_array_function(func)(*args, **kwargs) - return Array._wrap(result) - - return wrapped_func - - elif not wrap_return and wrap_args: - - @functools.wraps(func) - def wrapped_func(*args, **kwargs): - args = _wrap_args(args) - kwargs = _wrap_kwargs(kwargs) - return _wrap_array_function(func)(*args, **kwargs) - - return wrapped_func - - else: - - @functools.wraps(func) - def wrapped_func(*args, **kwargs): - return _wrap_array_function(func)(*args, **kwargs) - - return wrapped_func - - -def _wrap_decorator(func: Callable, wrap_return: bool = True, wrap_args: bool = True) -> Callable: - r"""Wrap a function decorator to work with :class:`Array`\s. - - Args: - func: A function to wrap. - wrap_return: If ``True`` convert results that are registered array backend types into - :class:`Array` objects. - wrap_args: If ``True`` also wrap function type args and kwargs of the wrapped function. - - Returns: - Callable: The wrapped function. - """ - # pylint: disable = function-redefined - if wrap_return and wrap_args: - - @functools.wraps(func) - def wrapped_func(*args, **kwargs): - args = _wrap_args(args) - kwargs = _wrap_kwargs(kwargs) - decorated = _wrap_array_function(func)(*args, **kwargs) - - @functools.wraps(args[0]) - def wrapped_decorated(*f_args, **f_kwargs): - f_args = _wrap_args(f_args) - f_kwargs = _wrap_kwargs(f_kwargs) - result = _wrap_function(decorated)(*f_args, **f_kwargs) - return Array._wrap(result) - - return wrapped_decorated - - return wrapped_func - - if wrap_return and not wrap_args: - - @functools.wraps(func) - def wrapped_func(*args, **kwargs): - decorated = _wrap_array_function(func)(*args, **kwargs) - - @functools.wraps(args[0]) - def wrapped_decorated(*f_args, **f_kwargs): - result = _wrap_function(decorated)(*f_args, **f_kwargs) - return Array._wrap(result) - - return wrapped_decorated - - return wrapped_func - - if not wrap_return and wrap_args: - - @functools.wraps(func) - def wrapped_func(*args, **kwargs): - args = _wrap_args(args) - kwargs = _wrap_kwargs(kwargs) - decorated = _wrap_array_function(func)(*args, **kwargs) - - @functools.wraps(args[0]) - def wrapped_decorated(*f_args, **f_kwargs): - f_args = _wrap_args(f_args) - f_kwargs = _wrap_kwargs(f_kwargs) - return _wrap_function(decorated)(*f_args, **f_kwargs) - - return wrapped_decorated - - return wrapped_func - - else: - - @functools.wraps(func) - def wrapped_func(*args, **kwargs): - decorated = _wrap_array_function(func)(*args, **kwargs) - - @functools.wraps(args[0]) - def wrapped_decorated(*f_args, **f_kwargs): - return _wrap_function(decorated)(*f_args, **f_kwargs) - - return wrapped_decorated - - return wrapped_func diff --git a/qiskit_dynamics/arraylias/alias.py b/qiskit_dynamics/arraylias/alias.py index d4e35390f..f90c7a2af 100644 --- a/qiskit_dynamics/arraylias/alias.py +++ b/qiskit_dynamics/arraylias/alias.py @@ -30,8 +30,6 @@ from qiskit import QiskitError -from qiskit_dynamics.array import Array - from .register_functions import ( register_asarray, register_matmul, @@ -46,10 +44,6 @@ DYNAMICS_NUMPY_ALIAS = numpy_alias() DYNAMICS_SCIPY_ALIAS = scipy_alias() -DYNAMICS_NUMPY_ALIAS.register_type(Array, "numpy") -DYNAMICS_SCIPY_ALIAS.register_type(Array, "numpy") - - DYNAMICS_NUMPY = DYNAMICS_NUMPY_ALIAS() DYNAMICS_SCIPY = DYNAMICS_SCIPY_ALIAS() diff --git a/qiskit_dynamics/dispatch/__init__.py b/qiskit_dynamics/dispatch/__init__.py deleted file mode 100644 index ab3a8e755..000000000 --- a/qiskit_dynamics/dispatch/__init__.py +++ /dev/null @@ -1,53 +0,0 @@ -# -*- coding: utf-8 -*- - -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -================================================= -Dispatch Module (:mod:`qiskit_dynamics.dispatch`) -================================================= - -.. currentmodule:: qiskit_dynamics.dispatch - -.. warning:: - - The ``array`` and ``dispatch`` submodules of Qiskit Dynamics have been deprecated as of version - 0.5.0. The use of the ``Array`` class is no longer required to work with different array - libraries in Qiskit Dynamics, and is broken in some cases. Refer to the :ref:`user guide entry - on using different array libraries with Qiskit Dynamics `. - Users can now work directly with the supported array type of their choice, without the need to - wrap them to enable dispatching. The ``array`` and ``dispatch`` submodules will be removed in - version 0.6.0. - - -This module contains dispatch methods used by the :class:`~qiskit_dynamics.array.Array` class. - - -Dispatch Functions -================== - -.. autosummary:: - :toctree: ../stubs/ - - asarray - requires_backend -""" - -# Import dispatch utilities -from .dispatch import ( - asarray, - requires_backend, -) - -# Register backends -from .backends import * diff --git a/qiskit_dynamics/dispatch/backends/__init__.py b/qiskit_dynamics/dispatch/backends/__init__.py deleted file mode 100644 index a2e42ed00..000000000 --- a/qiskit_dynamics/dispatch/backends/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -"""Register Dispatch backends""" - -from .numpy import * -from .jax import * - -# Set default backend if only 1 is available -from ..dispatch import Dispatch - -if len(Dispatch.REGISTERED_BACKENDS) == 1: - Dispatch.DEFAULT_BACKEND = Dispatch.REGISTERED_BACKENDS[0] - -__all__ = [] diff --git a/qiskit_dynamics/dispatch/backends/jax.py b/qiskit_dynamics/dispatch/backends/jax.py deleted file mode 100644 index 0d5d55936..000000000 --- a/qiskit_dynamics/dispatch/backends/jax.py +++ /dev/null @@ -1,81 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Register jax backend for Dispatch""" - -# pylint: disable=import-error - - -try: - import jax - from jax import Array - from jax.core import Tracer - - JAX_TYPES = (Array, Tracer) - - from ..dispatch import Dispatch - import numpy as np - from .numpy import _numpy_repr - - __all__ = [] - - # Custom handling of functions not in jax.numpy - HANDLED_FUNCTIONS = {} - - @Dispatch.register_asarray("jax", JAX_TYPES) - def _jax_asarray(array, dtype=None, order=None): - """Wrapper for jax.numpy.asarray""" - if ( - isinstance(array, JAX_TYPES) - and order is None - and (dtype is None or dtype == array.dtype) - ): - return array - return jax.numpy.asarray(array, dtype=dtype, order=order) - - @Dispatch.register_repr("jax") - def _jax_repr(array, prefix="", suffix=""): - """Wrapper for showing DeviceArray""" - if hasattr(array, "_value"): - return _numpy_repr(array._value, prefix=prefix, suffix=suffix) - return prefix + repr(array) + suffix - - @Dispatch.register_array_ufunc("jax") - def _jax_array_ufunc(ufunc, method): - """Wrapper mapping a numpy.ufunc to jax.numpy.ufunc""" - if method != "__call__": - return NotImplemented - name = ufunc.__name__ - if hasattr(jax.numpy, name): - return getattr(jax.numpy, name) - return NotImplemented - - @Dispatch.register_array_function("jax") - def _jax_array_function(func): - """Wrapper mapping a numpy function to jax.numpy function""" - if func in HANDLED_FUNCTIONS: - return HANDLED_FUNCTIONS[func] - name = func.__name__ - if hasattr(jax.numpy, name): - return getattr(jax.numpy, name) - if hasattr(jax.numpy.linalg, name): - return getattr(jax.numpy.linalg, name) - return NotImplemented - - # Custom function handling - - @Dispatch.implements(np.copy, HANDLED_FUNCTIONS) - def _copy(array, order="K"): - return jax.numpy.array(array, copy=True, order=order) - -except ModuleNotFoundError: - pass diff --git a/qiskit_dynamics/dispatch/backends/numpy.py b/qiskit_dynamics/dispatch/backends/numpy.py deleted file mode 100644 index 3efa6a44b..000000000 --- a/qiskit_dynamics/dispatch/backends/numpy.py +++ /dev/null @@ -1,66 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -"""Register numpy backend for Dispatch""" - -import numpy -from ..exceptions import DispatchError -from ..dispatch import Dispatch - -__all__ = [] - - -@Dispatch.register_asarray("numpy", numpy.ndarray) -def _numpy_asarray(array, dtype=None, order=None): - """Wrapper for numpy.asarray""" - if ( - isinstance(array, numpy.ndarray) - and order is None - and (dtype is None or (dtype == array.dtype and array.dtype != "O")) - ): - return array - ret = numpy.asarray(array, dtype=dtype, order=order) - if ret.dtype == "O": - raise DispatchError("Dispatch does not support numpy object arrays.") - return ret - - -@Dispatch.register_repr("numpy") -def _numpy_repr(array, prefix="", suffix=""): - """Wrapper for showing NumPy array in custom class""" - max_line_width = numpy.get_printoptions()["linewidth"] - array_str = numpy.array2string( - array, - separator=", ", - prefix=prefix, - suffix="," if suffix else None, - max_line_width=max_line_width, - ) - sep = "" - if len(suffix) > 1: - last_line_width = len(array_str) - array_str.rfind("\n") + 1 - if last_line_width + len(suffix) + 1 > max_line_width: - sep = ",\n" + " " * len(prefix) - else: - sep = ", " - return prefix + array_str + sep + suffix - - -@Dispatch.register_array_ufunc("numpy") -def _numpy_array_ufunc(ufunc, method): - """Trival wrapper for numpy.ufunc""" - return getattr(ufunc, method) - - -@Dispatch.register_array_function("numpy") -def _numpy_array_function(func): - """Trival wrapper for numpy array function""" - return func diff --git a/qiskit_dynamics/dispatch/dispatch.py b/qiskit_dynamics/dispatch/dispatch.py deleted file mode 100644 index e1e50fa16..000000000 --- a/qiskit_dynamics/dispatch/dispatch.py +++ /dev/null @@ -1,357 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -"""Dispatch class""" - -import functools -from types import FunctionType -from typing import Optional, Union, Tuple, Callable -from .exceptions import DispatchError - - -class Dispatch: - """Dispatch NumPy ufuncs to multiple array backends.""" - - # Registered backend names - REGISTERED_BACKENDS = tuple() - - # Set version of registered backend name strings - _REGISTERED_BACKENDS = set() - - # Registered backend array types - REGISTERED_TYPES = tuple() - - # Set version of registered backend array types - _REGISTERED_TYPES = {} - - # Default backend. If None the backend of the input array is used. - DEFAULT_BACKEND = None - - # Dispatch table from backend name to asarray function - ASARRAY_DISPATCH = {} - - # Dispatch table from backend name to repr function - REPR_DISPATCH = {} - - # Dispatch table from backend name to array_ufunc dispatcher - ARRAY_UFUNC_DISPATCH = {} - - # Dispatch table from backend name to array_function dispatcher - ARRAY_FUNCTION_DISPATCH = {} - - @classmethod - def backend( - cls, array: any, subclass: Optional[bool] = False, fallback: Optional[str] = None - ) -> str: - """Return the registered backend string of a array object. - - Args: - array: an array object. - subclass: If True check if array type is a subclass of - registered types. - fallback: Fallback backend to use if array does not match any - registered types. - - Returns: - str: The array backend name if the array type is registered, - or `use_default=True` and the type is not registered. - None: If `use_default=False` and the array type is not registered. - - Raises: - DispatchError: if fallback backend is not valid. - """ - backend = cls._REGISTERED_TYPES.get(type(array), None) - if backend is not None: - return backend - if subclass: - for key, backend in cls._REGISTERED_TYPES.items(): - if isinstance(array, key): - return backend - if fallback is None or fallback in cls._REGISTERED_BACKENDS: - return fallback - raise DispatchError(f"fallback '{fallback}' is not a registered backend.") - - @classmethod - def validate_backend(cls, backend: str): - """Raise an exception if backend is not registered. - - Args: - backend: array backend name. - - Raises: - DispatchError: if backend is not registered. - """ - if backend not in cls._REGISTERED_BACKENDS: - registered = cls.REGISTERED_BACKENDS if cls.REGISTERED_BACKENDS else None - raise DispatchError( - f"""'{backend}' is not a registered array backends - (registered backends: {registered})""" - ) - - @classmethod - def array_ufunc(cls, backend: str, ufunc: callable, method: str) -> callable: - """Return the ufunc for the specified array backend - - Args: - backend: the array backend name. - ufunc: the numpy ufunc. - method: the ufunc method. - - Returns: - callable: the ufunc for the specified array backend. - """ - return cls.ARRAY_UFUNC_DISPATCH[backend](ufunc, method) - - @classmethod - def repr(cls, backend: str) -> callable: - """Return the ufunc for the specified array backend - - Args: - backend: the array backend name. - - Returns: - callable: the repr function for the specified array backend. - """ - return cls.REPR_DISPATCH[backend] - - @classmethod - def array_function(cls, backend: str, func: callable) -> callable: - """Return the array function for the specified array backend - - Args: - backend: the array backend name. - func: the numpy array function. - - Returns: - callable: the function for the specified array backend. - """ - return cls.ARRAY_FUNCTION_DISPATCH[backend](func) - - @classmethod - def register_types(cls, name: str, array_types: Union[any, Tuple[any]]): - """Register an asarray array backend. - - Args: - name: A string to identify the array backend. - array_types: A class or list of classes to associate - with the array backend. - """ - cls._REGISTERED_BACKENDS.add(name) - cls.REGISTERED_BACKENDS = tuple(cls._REGISTERED_BACKENDS) - if not isinstance(array_types, (list, tuple)): - array_types = [array_types] - for atype in array_types: - cls._REGISTERED_TYPES[atype] = name - cls.REGISTERED_TYPES = tuple(cls._REGISTERED_TYPES.keys()) - - @classmethod - def register_asarray( - cls, name: str, array_types: Optional[Union[any, Tuple[any]]] = None - ) -> callable: - """Decorator to register an asarray function for an array backend. - - The function being wrapped must have signature - `func(arg, dtype=None, order=None)`. - - Args: - name: A string to identify the array backend. - array_types: Optional, the array types to register - for the backend. - - Returns: - callable: the decorated function. - """ - if array_types: - cls.register_types(name, array_types) - - def decorator(func): - cls.ASARRAY_DISPATCH[name] = func - return func - - return decorator - - @classmethod - def register_repr( - cls, name: str, array_types: Optional[Union[any, Tuple[any]]] = None - ) -> callable: - """Decorator to register an asarray function for an array backend. - - The function being wrapped must have signature - `func(arg, dtype=None, order=None)`. - - Args: - name: A string to identify the array backend. - array_types: Optional, the array types to register - for the backend. - - Returns: - callable: the decorated function. - """ - if array_types: - cls.register_types(name, array_types) - - def decorator(func): - cls.REPR_DISPATCH[name] = func - return func - - return decorator - - @classmethod - def register_array_ufunc( - cls, name: str, array_types: Optional[Union[any, Tuple[any]]] = None - ) -> callable: - """Decorator to register a ufunc dispatch function. - - This is used for handling of dispatch of NumPy ufuncs using - `__array_ufunc__`. The function being wrapped must have - signature `f(ufunc, method) -> callable(*args, **kwargs)` - - Args: - name: A string to identify the array backend. - array_types: Optional, the array types to register - for the backend. - - Returns: - callable: the decorated function. - """ - if array_types: - cls.register_types(name, array_types) - - def decorator(func): - cls.ARRAY_UFUNC_DISPATCH[name] = func - return func - - return decorator - - @classmethod - def register_array_function( - cls, name: str, array_types: Optional[Union[any, Tuple[any]]] = None - ) -> callable: - """Decorator to register an array function dispatch function. - - This is used for handling of dispatch of NumPy functions using - `__array_function__`. The function being wrapped must have - signature `f(func) -> callable(*args, **kwargs)`. - - Args: - name: A string to identify the array backend. - array_types: Optional, the array types to register - for the backend. - - Returns: - callable: the decorated function. - """ - if array_types: - cls.register_types(name, array_types) - - def decorator(func): - cls.ARRAY_FUNCTION_DISPATCH[name] = func - return func - - return decorator - - @staticmethod - def implements(np_function, dispatch_dict): - """Register a numpy __array_function__ implementation.""" - - def decorator(func): - dispatch_dict[np_function] = func - return func - - return decorator - - -def asarray( - array: any, - dtype: Optional[any] = None, - order: Optional[str] = None, - backend: Optional[str] = None, -) -> any: - """Convert input array to an array on the specified backend. - - This functions like `numpy.asarray` but optionally supports - casting to other registered array backends. - - Args: - array: An array_like input. - dtype: Optional. The dtype of the returned array. This value - must be supported by the specified array backend. - order: Optional. The array order. This value must be supported - by the specified array backend. - backend: A registered array backend name. If None the - default array backend will be used. - - Returns: - array: an array object of the form specified by the backend - kwarg. - """ - if backend: - Dispatch.validate_backend(backend) - else: - if Dispatch.DEFAULT_BACKEND: - backend = Dispatch.DEFAULT_BACKEND - else: - backend = Dispatch.backend(array, fallback="numpy") - return Dispatch.ASARRAY_DISPATCH[backend](array, dtype=dtype, order=order) - - -def requires_backend(backend: str) -> Callable: - """Return a function and class decorator for checking a backend is available. - - If the the required backend is not in the list of :func:`available_backends` - any decorated function or method will raise an exception when called, and - any decorated class will raise an exeption when its ``__init__`` is called. - - Args: - backend: the backend name required by class or function. - - Returns: - Callable: A decorator that may be used to specify that a function, class, - or class method requires a specific backend to be installed. - """ - - def decorator(obj): - """Specify that the decorated object requires a specifc Array backend.""" - - def check_backend(descriptor): - if backend not in Dispatch.REGISTERED_BACKENDS: - raise DispatchError( - f"Array backend '{backend}' required by {descriptor} " - "is not installed. Please install the optional " - f"library '{backend}'." - ) - - # Decorate a function or method - if isinstance(obj, FunctionType): - - @functools.wraps(obj) - def decorated_func(*args, **kwargs): - check_backend(f"function {obj}") - return obj(*args, **kwargs) - - return decorated_func - - # Decorate a class - elif isinstance(obj, type): - obj_init = obj.__init__ - - @functools.wraps(obj_init) - def decorated_init(self, *args, **kwargs): - check_backend(f"class {obj}") - obj_init(self, *args, **kwargs) - - obj.__init__ = decorated_init - return obj - - else: - raise ValueError(f"Cannot decorate object {obj} that is not a class or function.") - - return decorator diff --git a/qiskit_dynamics/dispatch/exceptions.py b/qiskit_dynamics/dispatch/exceptions.py deleted file mode 100644 index 9c78e9281..000000000 --- a/qiskit_dynamics/dispatch/exceptions.py +++ /dev/null @@ -1,27 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -"""Dispatch module exceptions""" - -from qiskit.exceptions import QiskitError - - -class DispatchError(QiskitError): - """Class for dispatch module errors.""" - - def __init__(self, *msg): - """Set the error message.""" - super().__init__(*msg) - self.msg = " ".join(msg) - - def __str__(self): - """Return the message.""" - return repr(self.msg) diff --git a/releasenotes/notes/array-dispatch-removed-9f691a2f243ac6e5.yaml b/releasenotes/notes/array-dispatch-removed-9f691a2f243ac6e5.yaml new file mode 100644 index 000000000..a4422951f --- /dev/null +++ b/releasenotes/notes/array-dispatch-removed-9f691a2f243ac6e5.yaml @@ -0,0 +1,4 @@ +--- +deprecations: + - | + The ``array`` and ``dispatch`` submodules have been removed. \ No newline at end of file diff --git a/test/dynamics/arraylias/test_alias.py b/test/dynamics/arraylias/test_alias.py index b9cb16f92..b2a9f7a6a 100644 --- a/test/dynamics/arraylias/test_alias.py +++ b/test/dynamics/arraylias/test_alias.py @@ -41,7 +41,7 @@ pass -@partial(test_array_backends, array_libraries=["numpy", "jax", "array_numpy", "array_jax"]) +@test_array_backends class TestDynamicsNumpy: """Test cases for global numpy configuration.""" diff --git a/test/dynamics/common.py b/test/dynamics/common.py index 11bdf26e7..fe1b92403 100644 --- a/test/dynamics/common.py +++ b/test/dynamics/common.py @@ -54,8 +54,6 @@ from qiskit_dynamics import DYNAMICS_NUMPY_ALIAS -from qiskit_dynamics.array import Array - def _is_sparse_object_array(A): return isinstance(A, np.ndarray) and A.ndim > 0 and issparse(A[0]) @@ -211,58 +209,6 @@ def jit_grad(self, func_to_test: Callable) -> Callable: return jit(grad(lambda *args: np.sum(func_to_test(*args)).real)) -class ArrayNumpyTestBase(QiskitDynamicsTestCase): - """Base class for tests working with qiskit_dynamics Arrays with numpy backend.""" - - @classmethod - def array_library(cls): - """Library method.""" - return "array_numpy" - - def asarray(self, a): - """Array generation method.""" - return Array(a) - - def assertArrayType(self, a): - """Assert the correct array type.""" - return isinstance(a, Array) and a.backend == "numpy" - - -class ArrayJaxTestBase(QiskitDynamicsTestCase): - """Base class for tests working with qiskit_dynamics Arrays with jax backend.""" - - @classmethod - def setUpClass(cls): - try: - # pylint: disable=import-outside-toplevel - import jax - - jax.config.update("jax_enable_x64", True) - jax.config.update("jax_platform_name", "cpu") - except Exception as err: - raise unittest.SkipTest("Skipping jax tests.") from err - - Array.set_default_backend("jax") - - @classmethod - def tearDownClass(cls): - """Set numpy back to the default backend.""" - Array.set_default_backend("numpy") - - @classmethod - def array_library(cls): - """Library method.""" - return "array_jax" - - def asarray(self, a): - """Array generation method.""" - return Array(a) - - def assertArrayType(self, a): - """Assert the correct array type.""" - return isinstance(a, Array) and a.backend == "jax" - - def test_array_backends(test_class: Type, array_libraries: Optional[List[str]] = None): """Test class decorator for different array backends. diff --git a/test/dynamics/signals/test_signals.py b/test/dynamics/signals/test_signals.py index 8e0a591ba..82ee86373 100644 --- a/test/dynamics/signals/test_signals.py +++ b/test/dynamics/signals/test_signals.py @@ -34,7 +34,7 @@ # pylint: disable=no-member -@partial(test_array_backends, array_libraries=["numpy", "jax", "array_numpy", "array_jax"]) +@test_array_backends class TestSignal: """Tests for Signal object.""" @@ -313,7 +313,7 @@ def test_conjugate(self): ) -@partial(test_array_backends, array_libraries=["numpy", "jax", "array_numpy", "array_jax"]) +@test_array_backends class TestConstant: """Tests for constant signal object.""" @@ -385,7 +385,7 @@ def test_conjugate(self): self.assertAllClose(const_conj(1.1), 3.0) -@partial(test_array_backends, array_libraries=["numpy", "jax", "array_numpy", "array_jax"]) +@test_array_backends class TestDiscreteSignal: """Tests for DiscreteSignal object.""" @@ -771,13 +771,11 @@ def test_empty_DiscreteSignal_to_sum(self): self.assertTrue(empty_sum.samples.shape == (1, 0)) -test_array_backends(TestSignalSum, array_libraries=["numpy", "jax", "array_numpy", "array_jax"]) -test_array_backends( - TestDiscreteSignalSum, array_libraries=["numpy", "jax", "array_numpy", "array_jax"] -) +test_array_backends(TestSignalSum) +test_array_backends(TestDiscreteSignalSum) -@partial(test_array_backends, array_libraries=["numpy", "jax", "array_numpy", "array_jax"]) +@test_array_backends class TestSignalList: """Test cases for SignalList class.""" @@ -840,7 +838,7 @@ def test_construction_with_numbers(self): self.assertAllClose(sig_list(3.0), self.asarray([4.0, 2.0, 3.0])) -@partial(test_array_backends, array_libraries=["numpy", "jax", "array_numpy", "array_jax"]) +@test_array_backends class TestSignalCollection: """Test cases for SignalCollection functionality."""