mirror of https://github.com/Qiskit/qiskit.git
Enable registration of add-on providers (#602)
* Allow wrapper.register to handle addon providers. This change should be backward compatible. * pass test * use new provider_name in provider registration * remove provider names. return provider handle when registering. default url in IBMQProvider * avoid double registration by implementing __eq__ * fix wrapper tests * fix wrapper tests * Capture all possible exceptions from providers at registering time. * Fix test_offline * Add changelog, linter
This commit is contained in:
parent
fc779c6094
commit
60032dd056
|
@ -74,7 +74,7 @@ disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax
|
|||
# disable the "too-many/few-..." refactoring hints
|
||||
too-many-lines, too-many-branches, too-many-locals, too-many-nested-blocks,
|
||||
too-many-statements, too-many-instance-attributes, too-many-arguments,
|
||||
too-many-public-methods, too-few-public-methods
|
||||
too-many-public-methods, too-few-public-methods, too-many-ancestors
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ The format is based on `Keep a Changelog`_.
|
|||
|
||||
Added
|
||||
-----
|
||||
- Retreive IBM Q jobs from server (#563, #585).
|
||||
- Retrieve IBM Q jobs from server (#563, #585).
|
||||
- Add German introductory documentation (``doc/de``) (#592).
|
||||
- Add ``unregister()`` for removing previously registered providers (#584).
|
||||
- Add matplotlib-based circuit drawer (#579).
|
||||
|
@ -31,7 +31,8 @@ Changed
|
|||
- Remove backend filtering in individual providers, keep only in wrapper (#575).
|
||||
- Single source of version information (#581)
|
||||
- Bumped IBMQuantumExperience dependency to 1.9.6 (#600).
|
||||
- For backend status, `status['available']` is now `status['operational']`.
|
||||
- For backend status, `status['available']` is now `status['operational']` (#609).
|
||||
- Added support for registering third-party providers in `register()` (#602).
|
||||
|
||||
Removed
|
||||
-------
|
||||
|
|
|
@ -659,14 +659,14 @@ class QuantumProgram(object):
|
|||
warnings.warn(
|
||||
"set_api() will be deprecated in upcoming versions (>0.5.0). "
|
||||
"Using qiskit.register() instead is recommended.", DeprecationWarning)
|
||||
qiskit.wrapper.register(token, url,
|
||||
hub, group, project, proxies, verify,
|
||||
provider_name='ibmq')
|
||||
qiskit.wrapper.register(token, url=url,
|
||||
hub=hub, group=group, project=project,
|
||||
proxies=proxies, verify=verify)
|
||||
|
||||
# TODO: the setting of self._api and self.__api_config is left for
|
||||
# backwards-compatibility.
|
||||
# pylint: disable=no-member
|
||||
self.__api = qiskit.wrapper._wrapper._DEFAULT_PROVIDER.providers['ibmq']._api
|
||||
self.__api = qiskit.wrapper._wrapper._DEFAULT_PROVIDER.providers[-1]._api
|
||||
config_dict = {
|
||||
'url': url,
|
||||
}
|
||||
|
|
|
@ -68,3 +68,13 @@ class BaseProvider(ABC):
|
|||
dict[str: str]: {deprecated_name: backend_name}
|
||||
"""
|
||||
return {}
|
||||
|
||||
def __eq__(self, other):
|
||||
"""
|
||||
Assumes two providers with the same class name clash.
|
||||
Derived providers can override this behavior
|
||||
(e.g. IBMQProvider instances are equal if and only if
|
||||
they have the same authentication attributes as well).
|
||||
"""
|
||||
equality = (type(self).__name__ == type(other).__name__)
|
||||
return equality
|
||||
|
|
|
@ -15,7 +15,7 @@ from qiskit.backends.ibmq.ibmqbackend import IBMQBackend
|
|||
|
||||
class IBMQProvider(BaseProvider):
|
||||
"""Provider for remote IbmQ backends."""
|
||||
def __init__(self, token, url,
|
||||
def __init__(self, token, url='https://quantumexperience.ng.bluemix.net/api',
|
||||
hub=None, group=None, project=None, proxies=None, verify=True):
|
||||
super().__init__()
|
||||
|
||||
|
@ -26,6 +26,15 @@ class IBMQProvider(BaseProvider):
|
|||
# Populate the list of remote backends.
|
||||
self.backends = self._discover_remote_backends()
|
||||
|
||||
# authentication attributes, which uniquely identify the provider instance
|
||||
self._token = token
|
||||
self._url = url
|
||||
self._hub = hub
|
||||
self._group = group
|
||||
self._project = project
|
||||
self._proxies = proxies
|
||||
self._verify = verify
|
||||
|
||||
def get_backend(self, name):
|
||||
return self.backends[name]
|
||||
|
||||
|
@ -124,3 +133,12 @@ class IBMQProvider(BaseProvider):
|
|||
ret[config['name']] = IBMQBackend(configuration=config, api=self._api)
|
||||
|
||||
return ret
|
||||
|
||||
def __eq__(self, other):
|
||||
try:
|
||||
equality = (self._token == other._token and self._url == other._url and
|
||||
self._hub == other._hub and self._group == other._group and
|
||||
self._project == other._project)
|
||||
except AttributeError:
|
||||
equality = False
|
||||
return equality
|
||||
|
|
|
@ -55,7 +55,8 @@ class LocalProvider(BaseProvider):
|
|||
def aliased_backend_names(self):
|
||||
return {
|
||||
'local_qasm_simulator': ['local_qasm_simulator_cpp',
|
||||
'local_qasm_simulator_py'],
|
||||
'local_qasm_simulator_py',
|
||||
'local_clifford_simulator_cpp'],
|
||||
'local_statevector_simulator': ['local_statevector_simulator_cpp',
|
||||
'local_statevector_simulator_py'],
|
||||
'local_unitary_simulator': ['local_unitary_simulator_cpp',
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
import warnings
|
||||
from qiskit import transpiler, QISKitError
|
||||
|
||||
from qiskit.backends.ibmq import IBMQProvider
|
||||
from qiskit.wrapper.defaultqiskitprovider import DefaultQISKitProvider
|
||||
from ._circuittoolkit import circuit_from_qasm_file, circuit_from_qasm_string
|
||||
|
||||
|
@ -19,13 +19,16 @@ from ._circuittoolkit import circuit_from_qasm_file, circuit_from_qasm_string
|
|||
_DEFAULT_PROVIDER = DefaultQISKitProvider()
|
||||
|
||||
|
||||
def register(token, url='https://quantumexperience.ng.bluemix.net/api',
|
||||
hub=None, group=None, project=None, proxies=None, verify=True,
|
||||
provider_name=None):
|
||||
def register(*args, provider_class=IBMQProvider, **kwargs):
|
||||
"""
|
||||
Authenticate against an online backend provider.
|
||||
This is a factory method that returns the provider that gets registered.
|
||||
|
||||
Args:
|
||||
args (tuple): positional arguments passed to provider class initialization
|
||||
provider_class (BaseProvider): provider class
|
||||
kwargs (dict): keyword arguments passed to provider class initialization.
|
||||
For the IBMQProvider default this can include things such as;
|
||||
token (str): The token used to register on the online backend such
|
||||
as the quantum experience.
|
||||
url (str): The url used for online backend such as the quantum
|
||||
|
@ -36,34 +39,43 @@ def register(token, url='https://quantumexperience.ng.bluemix.net/api',
|
|||
proxies (dict): Proxy configuration for the API, as a dict with
|
||||
'urls' and credential keys.
|
||||
verify (bool): If False, ignores SSL certificates errors.
|
||||
provider_name (str): the user-provided name for the registered
|
||||
provider.
|
||||
|
||||
Returns:
|
||||
BaseProvider: the provider instance that was just registered.
|
||||
|
||||
Raises:
|
||||
QISKitError: if the provider name is not recognized.
|
||||
QISKitError: if the provider could not be registered
|
||||
(e.g. due to conflict)
|
||||
"""
|
||||
# Convert the credentials to a dict.
|
||||
credentials = {
|
||||
'token': token, 'url': url, 'hub': hub, 'group': group,
|
||||
'project': project, 'proxies': proxies, 'verify': verify
|
||||
}
|
||||
_DEFAULT_PROVIDER.add_ibmq_provider(credentials, provider_name)
|
||||
try:
|
||||
provider = provider_class(*args, **kwargs)
|
||||
except Exception as ex:
|
||||
raise QISKitError("Couldn't instance provider!. Error: {0}".format(ex))
|
||||
|
||||
_DEFAULT_PROVIDER.add_provider(provider)
|
||||
return provider
|
||||
|
||||
|
||||
def unregister(provider_name):
|
||||
def unregister(provider):
|
||||
"""
|
||||
Removes a provider of list of registered providers.
|
||||
Removes a provider from list of registered providers.
|
||||
|
||||
Note:
|
||||
If backend names from provider1 and provider2 were clashing,
|
||||
`unregister(provider1)` removes the clash and makes the backends
|
||||
from provider2 available.
|
||||
|
||||
Args:
|
||||
provider_name (str): The unique name for the online provider.
|
||||
provider (BaseProvider): the provider instance to unregister
|
||||
Raises:
|
||||
QISKitError: if the provider name is not valid.
|
||||
QISKitError: if the provider instance is not registered
|
||||
"""
|
||||
_DEFAULT_PROVIDER.remove_provider(provider_name)
|
||||
_DEFAULT_PROVIDER.remove_provider(provider)
|
||||
|
||||
|
||||
def registered_providers():
|
||||
"""Return the names of the currently registered providers."""
|
||||
return list(_DEFAULT_PROVIDER.providers.keys())
|
||||
"""Return the currently registered providers."""
|
||||
return list(_DEFAULT_PROVIDER.providers)
|
||||
|
||||
|
||||
# Functions for inspecting and retrieving backends.
|
||||
|
@ -78,6 +90,18 @@ def available_backends(filters=None, compact=True):
|
|||
In order for this function to return online backends, a connection with
|
||||
an online backend provider needs to be established by calling the
|
||||
`register()` function.
|
||||
|
||||
Note:
|
||||
If two or more providers have backends with the same name, those names
|
||||
will be shown only once. To disambiguate and choose a backend from a
|
||||
specific provider, get the backend from that specific provider.
|
||||
|
||||
Example:
|
||||
p1 = register(token1)
|
||||
p2 = register(token2)
|
||||
execute(circuit, p1.get_backend('ibmq_5_tenerife'))
|
||||
execute(circuit, p2.get_backend('ibmq_5_tenerife'))
|
||||
|
||||
Args:
|
||||
filters (dict or callable): filtering conditions.
|
||||
compact (bool): group backend names based on compact group names.
|
||||
|
|
|
@ -8,12 +8,10 @@
|
|||
"""Meta-provider that aggregates several providers."""
|
||||
|
||||
import logging
|
||||
from collections import OrderedDict
|
||||
from itertools import combinations
|
||||
|
||||
from qiskit import QISKitError
|
||||
from qiskit.backends.baseprovider import BaseProvider
|
||||
from qiskit.backends.ibmq import IBMQProvider
|
||||
from qiskit.backends.local.localprovider import LocalProvider
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -26,12 +24,12 @@ class DefaultQISKitProvider(BaseProvider):
|
|||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
# Dict of providers.
|
||||
self.providers = OrderedDict({'local': LocalProvider()})
|
||||
# List of providers.
|
||||
self.providers = [LocalProvider()]
|
||||
|
||||
def get_backend(self, name):
|
||||
name = self.resolve_backend_name(name)
|
||||
for provider in self.providers.values():
|
||||
for provider in self.providers:
|
||||
try:
|
||||
return provider.get_backend(name)
|
||||
except KeyError:
|
||||
|
@ -41,6 +39,10 @@ class DefaultQISKitProvider(BaseProvider):
|
|||
def available_backends(self, filters=None):
|
||||
"""Get a list of available backends from all providers (after filtering).
|
||||
|
||||
Note:
|
||||
If two or more providers share similar backend names, only the backends
|
||||
belonging to the 1st registered provider will be returned.
|
||||
|
||||
Args:
|
||||
filters (dict or callable): filtering conditions.
|
||||
each will either pass through, or be filtered out.
|
||||
|
@ -59,7 +61,7 @@ class DefaultQISKitProvider(BaseProvider):
|
|||
"""
|
||||
# pylint: disable=arguments-differ
|
||||
backends = []
|
||||
for provider in self.providers.values():
|
||||
for provider in self.providers:
|
||||
backends.extend(provider.available_backends())
|
||||
|
||||
if filters is not None:
|
||||
|
@ -97,7 +99,7 @@ class DefaultQISKitProvider(BaseProvider):
|
|||
ValueError: if a backend is mapped to multiple aliases
|
||||
"""
|
||||
aliases = {}
|
||||
for provider in self.providers.values():
|
||||
for provider in self.providers:
|
||||
aliases = {**aliases, **provider.aliased_backend_names()}
|
||||
for pair in combinations(aliases.values(), r=2):
|
||||
if not set.isdisjoint(set(pair[0]), set(pair[1])):
|
||||
|
@ -113,94 +115,57 @@ class DefaultQISKitProvider(BaseProvider):
|
|||
dict[str: list[str]]: aggregated alias dictionary
|
||||
"""
|
||||
deprecates = {}
|
||||
for provider in self.providers.values():
|
||||
for provider in self.providers:
|
||||
deprecates = {**deprecates, **provider.deprecated_backend_names()}
|
||||
|
||||
return deprecates
|
||||
|
||||
def add_provider(self, provider, provider_name):
|
||||
def add_provider(self, provider):
|
||||
"""
|
||||
Add a new provider to the list of known providers.
|
||||
|
||||
Note:
|
||||
If some backend in the new provider has a name in use by an
|
||||
already registered provider, the backend will not be available,
|
||||
and the name of the backend will still refer to that previously
|
||||
registered.
|
||||
|
||||
Args:
|
||||
provider (BaseProvider): Provider instance.
|
||||
provider_name (str): User-provided name for the provider.
|
||||
|
||||
Returns:
|
||||
BaseProvider: the provider instance.
|
||||
|
||||
Raises:
|
||||
QISKitError: if a provider with the same name is already in the
|
||||
list.
|
||||
QISKitError: if trying to add a provider identical to one already registered
|
||||
"""
|
||||
if provider_name in self.providers.keys():
|
||||
raise QISKitError(
|
||||
'A provider with name "%s" is already registered.'
|
||||
% provider_name)
|
||||
|
||||
# Check for backend name clashes, emitting a warning.
|
||||
current_backends = {str(backend) for backend in self.available_backends()}
|
||||
added_backends = {str(backend) for backend in provider.available_backends()}
|
||||
common_backends = added_backends.intersection(current_backends)
|
||||
if common_backends:
|
||||
logger.warning(
|
||||
'The backend names "%s" (provided by "%s") are already in use. '
|
||||
'Consider using unregister() for avoiding name conflicts.',
|
||||
list(common_backends), provider_name)
|
||||
'The backend names "%s" of this provider are already in use. '
|
||||
'Refer to documentation for `available_backends()` and `unregister()`.',
|
||||
list(common_backends))
|
||||
|
||||
self.providers[provider_name] = provider
|
||||
# checks for equality of provider instances, based on the __eq__ method
|
||||
if provider not in self.providers:
|
||||
self.providers.append(provider)
|
||||
else:
|
||||
raise QISKitError("The same provider has already been registered!")
|
||||
|
||||
return provider
|
||||
|
||||
def add_ibmq_provider(self, credentials_dict, provider_name=None):
|
||||
"""
|
||||
Add a new IBMQProvider to the list of known providers.
|
||||
|
||||
Args:
|
||||
credentials_dict (dict): dictionary of credentials for a provider.
|
||||
provider_name (str): User-provided name for the provider. A name
|
||||
will automatically be assigned if possible.
|
||||
Raises:
|
||||
QISKitError: if a provider with the same name is already in the
|
||||
list; or if a provider name could not be assigned.
|
||||
Returns:
|
||||
IBMQProvider: the new IBMQProvider instance.
|
||||
"""
|
||||
# Automatically assign a name if not specified.
|
||||
if not provider_name:
|
||||
if 'quantumexperience' in credentials_dict['url']:
|
||||
provider_name = 'ibmq'
|
||||
elif 'q-console' in credentials_dict['url']:
|
||||
provider_name = 'qnet'
|
||||
else:
|
||||
raise QISKitError(
|
||||
'Cannot parse provider name from credentials.')
|
||||
|
||||
ibmq_provider = IBMQProvider(**credentials_dict)
|
||||
|
||||
return self.add_provider(ibmq_provider, provider_name)
|
||||
|
||||
def remove_provider(self, provider_name):
|
||||
def remove_provider(self, provider):
|
||||
"""
|
||||
Remove a provider from the list of known providers.
|
||||
|
||||
Args:
|
||||
provider_name (str): name of the provider to be removed.
|
||||
provider (BaseProvider): provider to be removed.
|
||||
|
||||
Raises:
|
||||
QISKitError: if the provider name is not valid.
|
||||
QISKitError: if the provider is not registered.
|
||||
"""
|
||||
if provider_name == 'local':
|
||||
if isinstance(provider, LocalProvider):
|
||||
raise QISKitError("Cannot unregister 'local' provider.")
|
||||
try:
|
||||
self.providers.pop(provider_name)
|
||||
except KeyError:
|
||||
self.providers.remove(provider)
|
||||
except ValueError:
|
||||
raise QISKitError("'%s' provider is not registered.")
|
||||
|
||||
def resolve_backend_name(self, name):
|
||||
|
|
|
@ -1449,7 +1449,7 @@ class TestQuantumProgram(QiskitTestCase):
|
|||
''.join(random.choice(string.ascii_lowercase) for _ in range(63))
|
||||
)
|
||||
# SDK will throw ConnectionError on every call that implies a connection
|
||||
self.assertRaises(ConnectionError, qp.set_api, FAKE_TOKEN, FAKE_URL)
|
||||
self.assertRaises(QISKitError, qp.set_api, FAKE_TOKEN, FAKE_URL)
|
||||
|
||||
def test_results_save_load(self):
|
||||
"""Test saving and loading the results of a circuit.
|
||||
|
@ -1597,8 +1597,7 @@ class TestQuantumProgram(QiskitTestCase):
|
|||
# TODO: use the backend directly when the deprecation is completed.
|
||||
from ._mockutils import DummyProvider
|
||||
import qiskit.wrapper
|
||||
qiskit.wrapper._wrapper._DEFAULT_PROVIDER.add_provider(DummyProvider(),
|
||||
'dummy')
|
||||
qiskit.wrapper._wrapper._DEFAULT_PROVIDER.add_provider(DummyProvider())
|
||||
|
||||
q_program = QuantumProgram(specs=self.QPS_SPECS)
|
||||
qr = q_program.get_quantum_register("q_name")
|
||||
|
|
|
@ -14,6 +14,7 @@ import unittest
|
|||
|
||||
import qiskit.wrapper
|
||||
from qiskit.wrapper import registered_providers
|
||||
from qiskit.backends.ibmq import IBMQProvider
|
||||
from qiskit import QISKitError
|
||||
from .common import QiskitTestCase, requires_qe_access
|
||||
from .test_backends import remove_backends_from_list
|
||||
|
@ -24,8 +25,7 @@ class TestWrapper(QiskitTestCase):
|
|||
@requires_qe_access
|
||||
def test_wrapper_register_ok(self, QE_TOKEN, QE_URL, hub, group, project):
|
||||
"""Test wrapper.register()."""
|
||||
qiskit.wrapper.register(QE_TOKEN, QE_URL, hub, group, project,
|
||||
provider_name='ibmq')
|
||||
qiskit.wrapper.register(QE_TOKEN, QE_URL, hub, group, project)
|
||||
backends = qiskit.wrapper.available_backends()
|
||||
backends = remove_backends_from_list(backends)
|
||||
self.log.info(backends)
|
||||
|
@ -34,8 +34,7 @@ class TestWrapper(QiskitTestCase):
|
|||
@requires_qe_access
|
||||
def test_backends_with_filter(self, QE_TOKEN, QE_URL, hub, group, project):
|
||||
"""Test wrapper.available_backends(filter=...)."""
|
||||
qiskit.wrapper.register(QE_TOKEN, QE_URL, hub, group, project,
|
||||
provider_name='ibmq')
|
||||
qiskit.wrapper.register(QE_TOKEN, QE_URL, hub, group, project)
|
||||
backends = qiskit.wrapper.available_backends({'local': False,
|
||||
'simulator': True})
|
||||
self.log.info(backends)
|
||||
|
@ -56,20 +55,8 @@ class TestWrapper(QiskitTestCase):
|
|||
qiskit.wrapper.register(QE_TOKEN, QE_URL, hub, group, project)
|
||||
self.assertCountEqual(initial_providers, registered_providers())
|
||||
|
||||
@requires_qe_access
|
||||
def test_register_twice_with_different_names(self, QE_TOKEN, QE_URL,
|
||||
hub, group, project):
|
||||
"""Test double registration of same credentials but different names."""
|
||||
initial_providers = registered_providers()
|
||||
qiskit.wrapper.register(QE_TOKEN, QE_URL, hub, group, project,
|
||||
provider_name='provider1')
|
||||
qiskit.wrapper.register(QE_TOKEN, QE_URL, hub, group, project,
|
||||
provider_name='provider2')
|
||||
self.assertCountEqual(initial_providers + ['provider1', 'provider2'],
|
||||
registered_providers())
|
||||
|
||||
def test_register_unknown_name(self):
|
||||
"""Test registering a provider with not explicit name."""
|
||||
def test_register_bad_credentials(self):
|
||||
"""Test registering a provider with bad credentials."""
|
||||
initial_providers = registered_providers()
|
||||
with self.assertRaises(QISKitError):
|
||||
qiskit.wrapper.register('FAKE_TOKEN', 'http://unknown')
|
||||
|
@ -79,38 +66,36 @@ class TestWrapper(QiskitTestCase):
|
|||
def test_unregister(self, QE_TOKEN, QE_URL, hub, group, project):
|
||||
"""Test unregistering."""
|
||||
initial_providers = registered_providers()
|
||||
qiskit.wrapper.register(QE_TOKEN, QE_URL, hub, group, project,
|
||||
provider_name='provider1')
|
||||
self.assertCountEqual(initial_providers + ['provider1'],
|
||||
ibmqprovider = qiskit.wrapper.register(QE_TOKEN, QE_URL, hub, group, project)
|
||||
self.assertCountEqual(initial_providers + [ibmqprovider],
|
||||
registered_providers())
|
||||
qiskit.wrapper.unregister('provider1')
|
||||
qiskit.wrapper.unregister(ibmqprovider)
|
||||
self.assertEqual(initial_providers, registered_providers())
|
||||
|
||||
def test_unregister_non_existent(self):
|
||||
@requires_qe_access
|
||||
def test_unregister_non_existent(self, QE_TOKEN, QE_URL, hub, group, project):
|
||||
"""Test unregistering a non existent provider."""
|
||||
initial_providers = registered_providers()
|
||||
ibmqprovider = IBMQProvider(QE_TOKEN, QE_URL, hub, group, project)
|
||||
with self.assertRaises(QISKitError):
|
||||
qiskit.wrapper.unregister('provider1')
|
||||
qiskit.wrapper.unregister(ibmqprovider)
|
||||
self.assertEqual(initial_providers, registered_providers())
|
||||
|
||||
@requires_qe_access
|
||||
def test_register_backend_name_conflicts(self, QE_TOKEN, QE_URL,
|
||||
hub, group, project):
|
||||
"""Test backend name conflicts when registering."""
|
||||
|
||||
qiskit.wrapper.register(QE_TOKEN, QE_URL, hub, group, project,
|
||||
provider_name='provider1')
|
||||
qiskit.wrapper.register(QE_TOKEN, QE_URL, hub, group, project)
|
||||
initial_providers = registered_providers()
|
||||
initial_backends = qiskit.wrapper.available_backends()
|
||||
ibmqx4_backend = qiskit.wrapper.get_backend('ibmqx4')
|
||||
with self.assertLogs(level=logging.WARNING) as logs:
|
||||
qiskit.wrapper.register(QE_TOKEN, QE_URL, hub, group, project,
|
||||
provider_name='provider2')
|
||||
ibmqprovider2 = qiskit.wrapper.register(QE_TOKEN, QE_URL, hub, group, project)
|
||||
|
||||
# Check that one, and only one warning has been issued.
|
||||
self.assertEqual(len(logs.records), 1)
|
||||
# Check that the provider has been registered.
|
||||
self.assertCountEqual(initial_providers + ['provider2'],
|
||||
self.assertCountEqual(initial_providers + [ibmqprovider2],
|
||||
registered_providers())
|
||||
# Check that no new backends have been added.
|
||||
self.assertCountEqual(initial_backends,
|
||||
|
|
Loading…
Reference in New Issue