diff --git a/qiskit/providers/__init__.py b/qiskit/providers/__init__.py index 643474f76e84..a0d103266abd 100644 --- a/qiskit/providers/__init__.py +++ b/qiskit/providers/__init__.py @@ -82,7 +82,9 @@ :toctree: ../stubs/ Provider + ProviderV2 ProviderV1 + BackendList Backend ------- @@ -152,6 +154,8 @@ # Providers interface from qiskit.providers.provider import Provider from qiskit.providers.provider import ProviderV1 +from qiskit.providers.provider import ProviderV2 +from qiskit.providers.provider import BackendList from qiskit.providers.backend import Backend from qiskit.providers.backend import BackendV1 from qiskit.providers.options import Options diff --git a/qiskit/providers/provider.py b/qiskit/providers/provider.py index dfa1ab45a199..3d11ccb354af 100644 --- a/qiskit/providers/provider.py +++ b/qiskit/providers/provider.py @@ -75,3 +75,108 @@ def __eq__(self, other): equal. Subclassed providers can override this behavior. """ return type(self).__name__ == type(other).__name__ + + +class ProviderV2(Provider, ABC): + """Base class for a Backend Provider.""" + version = 2 + + def get_backend(self, name=None, **kwargs): + """Return a single backend matching the specified filtering. + + Args: + name (str): name of the backend. + **kwargs: dict used for filtering. + + Returns: + Backend: a backend matching the filtering. + + Raises: + QiskitBackendNotFoundError: if no backend could be found or + more than one backend matches the filtering criteria. + """ + backends = self.backends(name, **kwargs) + if len(backends) > 1: + raise QiskitBackendNotFoundError('More than one backend matches the criteria') + if not backends: + raise QiskitBackendNotFoundError('No backend matches the criteria') + + return backends[0] + + @abstractmethod + @property + def backends(self): + """The backends provided by this provider. + + This can be accessed by name via attributes for example:: + + my_provider = Provider() + my_provider.backend.backend_name + + or called with the signature that matches + :meth:`~qiskit.providers.ProviderV1.backends`, for example:: + + my_provider = Provider() + my_provider.backend(name='backend_name') + + Returns: + BackendsList: A :class:`~qiskit.providers.BackendsList` object + """ + pass + + def __eq__(self, other): + """Equality comparison. + + By default, it is assumed that two `Providers` from the same class are + equal. Subclassed providers can override this behavior. + """ + return type(self).__name__ == type(other).__name__ + + +class BackendList: + """A service class that allows for autocompletion of backends from a provider. + + Each backend can be accessed as attribute by backend directly for example for two + backends ``BackendA`` (with a name ``'backend_a'``) and ``BackendB`` (with + a name ``'backend_b'``):: + + from qiskit.providers import BackendV1 + + backends = BackendList([BackendA, BackendB]) + backend_a = backends.backend_a.configuration() + + would get the backend configuration object for backend_a. For backwards + compatibility a ``BackendList`` object is callable just as + :meth:`qiskit.providers.ProviderV1.backends`. For example:: + + backends = BackendList([BackendA, BackendB]) + backend_list = backends(name='backend_a') + + """ + + def __init__(self, backends): + """Initialize a new ``BackendList`` object. + + Args: + backends (list): List of :class:`~qiskit.providers.Backend` instances. + """ + self._backends = backends + for backend in backends: + setattr(self, backend.name(), backend) + + def __call__(self, name=None, filters=None, **kwargs): + """A listing of all backends from this provider. + + Args: + name (str): The name of a given backend. + filters (callable): A filter function. + Returns: + list: A list of backends, if any. + """ + # pylint: disable=arguments-differ + backends = self._backends + if name: + backends = [ + backend for backend in backends if backend.name() == name] + + return filter_backends(backends, filters=filters, **kwargs) diff --git a/releasenotes/notes/add-provider-v2-b00575214369d6df.yaml b/releasenotes/notes/add-provider-v2-b00575214369d6df.yaml new file mode 100644 index 000000000000..b064c1da29a9 --- /dev/null +++ b/releasenotes/notes/add-provider-v2-b00575214369d6df.yaml @@ -0,0 +1,13 @@ +--- +features: + - | + A new version of the :class:`~qiskit.providers.Provider` abstract interface, + :class:`~qiskit.providers.ProviderV2`. This new version is fully compatible + with v1 except that :attr:`~qiskit.providers.ProviderV2.backends` is an + attribute that contains a :class:`~qiskit.providers.BackendsList` object + which is callable to retain the behavior of the + :class:`~qiskit.providers.ProviderV1` method + :meth:`~qiskit.providers.ProviderV1.backends` but also enables attribute + access by name for all the backends provided by the ``Provider``. + Implementers of :class:`~qiskit.providers.Provider` should upgrade to + this new version to add the extra functionality.