Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix QuantumInstance compatibility with BackendV2 (backport #7563) #7605

Merged
merged 2 commits into from
Feb 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions qiskit/providers/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,15 @@ def options(self):
"""
return self._options

@property
def provider(self):
"""Return the backend Provider.

Returns:
Provider: the Provider responsible for the backend.
"""
return self._provider

@abstractmethod
def run(self, run_input, **options):
"""Run on the backend.
Expand Down
41 changes: 41 additions & 0 deletions qiskit/test/mock/fake_backend_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,14 @@
UGate,
ECRGate,
RXGate,
SXGate,
XGate,
RZGate,
)
from qiskit.providers.backend import BackendV2, QubitProperties
from qiskit.providers.options import Options
from qiskit.transpiler import Target, InstructionProperties
from qiskit.providers.basicaer.qasm_simulator import QasmSimulatorPy


class FakeBackendV2(BackendV2):
Expand Down Expand Up @@ -176,3 +180,40 @@ def qubit_properties(self, qubit):
if isinstance(qubit, int):
return self._qubit_properties[qubit]
return [self._qubit_properties[i] for i in qubit]


class FakeBackendSimple(BackendV2):
"""A fake simple backend that wraps BasicAer to implement run()."""

def __init__(self):
super().__init__(
None,
name="FakeSimpleV2",
description="A fake simple BackendV2 example",
online_date=datetime.datetime.utcnow(),
backend_version="0.0.1",
)
self._lam = Parameter("lambda")
self._target = Target(num_qubits=20)
self._target.add_instruction(SXGate())
self._target.add_instruction(XGate())
self._target.add_instruction(RZGate(self._lam))
self._target.add_instruction(CXGate())
self._target.add_instruction(Measure())
self._runner = QasmSimulatorPy()

@property
def target(self):
return self._target

@property
def max_circuits(self):
return None

@classmethod
def _default_options(cls):
return QasmSimulatorPy._default_options()

def run(self, run_input, **options):
self._runner._options = self._options
return self._runner.run(run_input, **options)
43 changes: 37 additions & 6 deletions qiskit/utils/backend_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,25 @@ def __init__(self) -> None:
_PROVIDER_CHECK = ProviderCheck()


def _get_backend_interface_version(backend):
"""Get the backend version int."""
backend_interface_version = getattr(backend, "version", None)
# Handle deprecated BaseBackend based backends which have a version()
# method
if not isinstance(backend_interface_version, int):
backend_interface_version = 0
return backend_interface_version


def _get_backend_provider(backend):
backend_interface_version = _get_backend_interface_version(backend)
if backend_interface_version > 1:
provider = backend.provider
else:
provider = backend.provider()
return provider


def has_ibmq():
"""Check if IBMQ is installed"""
if not _PROVIDER_CHECK.checked_ibmq:
Expand Down Expand Up @@ -78,7 +97,7 @@ def is_aer_provider(backend):
if has_aer():
from qiskit.providers.aer import AerProvider

if isinstance(backend.provider(), AerProvider):
if isinstance(_get_backend_provider(backend), AerProvider):
return True
from qiskit.providers.aer.backends.aerbackend import AerBackend

Expand All @@ -97,7 +116,7 @@ def is_basicaer_provider(backend):
"""
from qiskit.providers.basicaer import BasicAerProvider

return isinstance(backend.provider(), BasicAerProvider)
return isinstance(_get_backend_provider(backend), BasicAerProvider)


def is_ibmq_provider(backend):
Expand All @@ -111,7 +130,7 @@ def is_ibmq_provider(backend):
if has_ibmq():
from qiskit.providers.ibmq.accountprovider import AccountProvider

return isinstance(backend.provider(), AccountProvider)
return isinstance(_get_backend_provider(backend), AccountProvider)

return False

Expand Down Expand Up @@ -144,7 +163,13 @@ def is_statevector_backend(backend):
return True
if isinstance(backend, AerSimulator) and backend.name() == "aer_simulator_statevector":
return True
return backend.name().startswith("statevector") if backend is not None else False
if backend is None:
return False
backend_interface_version = _get_backend_interface_version(backend)
if backend_interface_version <= 1:
return backend.name().startswith("statevector")
else:
return backend.name.startswith("statevector")


def is_simulator_backend(backend):
Expand All @@ -156,7 +181,10 @@ def is_simulator_backend(backend):
Returns:
bool: True is a simulator
"""
return backend.configuration().simulator
backend_interface_version = _get_backend_interface_version(backend)
if backend_interface_version <= 1:
return backend.configuration().simulator
return False


def is_local_backend(backend):
Expand All @@ -168,7 +196,10 @@ def is_local_backend(backend):
Returns:
bool: True is a local backend
"""
return backend.configuration().local
backend_interface_version = _get_backend_interface_version(backend)
if backend_interface_version <= 1:
return backend.configuration().local
return False


def is_aer_qasm(backend):
Expand Down
35 changes: 22 additions & 13 deletions qiskit/utils/quantum_instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
is_aer_qasm,
is_basicaer_provider,
support_backend_options,
_get_backend_provider,
_get_backend_interface_version,
)
from qiskit.utils.mitigation import (
CompleteMeasFitter,
Expand Down Expand Up @@ -237,15 +239,16 @@ def __init__(
QiskitError: set backend_options but the backend does not support that
"""
self._backend = backend
self._backend_interface_version = _get_backend_interface_version(self._backend)
self._pass_manager = pass_manager
self._bound_pass_manager = bound_pass_manager

# if the shots are none, try to get them from the backend
if shots is None:
from qiskit.providers.basebackend import BaseBackend # pylint: disable=cyclic-import
from qiskit.providers.backend import BackendV1 # pylint: disable=cyclic-import
from qiskit.providers.backend import Backend # pylint: disable=cyclic-import

if isinstance(backend, (BaseBackend, BackendV1)):
if isinstance(backend, (BaseBackend, Backend)):
if hasattr(backend, "options"): # should always be true for V1
backend_shots = backend.options.get("shots", 1024)
if shots != backend_shots:
Expand All @@ -269,9 +272,12 @@ def __init__(
self._run_config = run_config

# setup backend config
basis_gates = basis_gates or backend.configuration().basis_gates
coupling_map = coupling_map or getattr(backend.configuration(), "coupling_map", None)
self._backend_config = {"basis_gates": basis_gates, "coupling_map": coupling_map}
if self._backend_interface_version <= 1:
basis_gates = basis_gates or backend.configuration().basis_gates
coupling_map = coupling_map or getattr(backend.configuration(), "coupling_map", None)
self._backend_config = {"basis_gates": basis_gates, "coupling_map": coupling_map}
else:
self._backend_config = {}

# setup compile config
self._compile_config = {
Expand All @@ -295,7 +301,7 @@ def __init__(
"The noise model is not supported "
"on the selected backend {} ({}) "
"only certain backends, such as Aer qasm simulator "
"support noise.".format(self.backend_name, self._backend.provider())
"support noise.".format(self.backend_name, _get_backend_provider(self._backend))
)

# setup backend options for run
Expand Down Expand Up @@ -363,7 +369,7 @@ def __str__(self) -> str:
info = f"\nQiskit Terra version: {terra_version}\n"
info += "Backend: '{} ({})', with following setting:\n{}\n{}\n{}\n{}\n{}\n{}".format(
self.backend_name,
self._backend.provider(),
_get_backend_provider(self._backend),
self._backend_config,
self._compile_config,
self._run_config,
Expand Down Expand Up @@ -494,10 +500,10 @@ def execute(self, circuits, had_transpiled: bool = False):
# transpile here, the method always returns a copied list
circuits = self.transpile(circuits)

from qiskit.providers import BackendV1
from qiskit.providers import Backend

circuit_job = isinstance(self._backend, BackendV1)
if self.is_statevector and self._backend.name() == "aer_simulator_statevector":
circuit_job = isinstance(self._backend, Backend)
if self.is_statevector and self.backend_name == "aer_simulator_statevector":
try:
from qiskit.providers.aer.library import SaveStatevector

Expand Down Expand Up @@ -825,7 +831,7 @@ def set_config(self, **kwargs):
if not support_backend_options(self._backend):
raise QiskitError(
"backend_options can not be used with this backend "
"{} ({}).".format(self.backend_name, self._backend.provider())
"{} ({}).".format(self.backend_name, _get_backend_provider(self._backend))
)

if k in QuantumInstance._BACKEND_OPTIONS_QASM_ONLY and self.is_statevector:
Expand All @@ -842,7 +848,7 @@ def set_config(self, **kwargs):
raise QiskitError(
"The noise model is not supported on the selected backend {} ({}) "
"only certain backends, such as Aer qasm support "
"noise.".format(self.backend_name, self._backend.provider())
"noise.".format(self.backend_name, _get_backend_provider(self._backend))
)

self._noise_config[k] = v
Expand Down Expand Up @@ -956,7 +962,10 @@ def backend(self):
@property
def backend_name(self):
"""Return backend name."""
return self._backend.name()
if self._backend_interface_version <= 1:
return self._backend.name()
else:
return self._backend.name

@property
def is_statevector(self):
Expand Down
26 changes: 21 additions & 5 deletions qiskit/utils/run_circuits.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
is_simulator_backend,
is_local_backend,
is_ibmq_provider,
_get_backend_interface_version,
)

MAX_CIRCUITS_PER_JOB = os.environ.get("QISKIT_AQUA_MAX_CIRCUITS_PER_JOB", None)
Expand Down Expand Up @@ -277,7 +278,11 @@ def run_qobj(
if is_local_backend(backend):
max_circuits_per_job = sys.maxsize
else:
max_circuits_per_job = backend.configuration().max_experiments
backend_interface_version = _get_backend_interface_version(backend)
if backend_interface_version <= 1:
max_circuits_per_job = backend.configuration().max_experiments
else:
max_circuits_per_job = backend.max_circuits

# split qobj if it exceeds the payload of the backend

Expand Down Expand Up @@ -470,18 +475,29 @@ def run_circuits(
Raises:
QiskitError: Any error except for JobError raised by Qiskit Terra
"""
backend_interface_version = _get_backend_interface_version(backend)

backend_options = backend_options or {}
noise_config = noise_config or {}
run_config = run_config or {}
with_autorecover = not is_simulator_backend(backend)
if backend_interface_version <= 1:
with_autorecover = not is_simulator_backend(backend)
else:
with_autorecover = False

if MAX_CIRCUITS_PER_JOB is not None:
max_circuits_per_job = int(MAX_CIRCUITS_PER_JOB)
else:
if is_local_backend(backend):
max_circuits_per_job = sys.maxsize
if backend_interface_version <= 1:
if is_local_backend(backend):
max_circuits_per_job = sys.maxsize
else:
max_circuits_per_job = backend.configuration().max_experiments
else:
max_circuits_per_job = backend.configuration().max_experiments
if backend.max_circuits is not None:
max_circuits_per_job = backend.max_circuits
else:
max_circuits_per_job = sys.maxsize

if len(circuits) > max_circuits_per_job:
jobs = []
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
fixes:
- |
Added a missing :attr:`.BackendV2.provider` attribute to implementations
of the :class:`.BackendV2` abstract class. Previously, :class:`.BackendV2`
backends could be initialized with a provider but that was not accesible
to users.
- |
Fixed support for the :class:`.QuantumInstance` class when running with
a :class:`.BackendV2` backend. Previously, attempting to use a
:class:`.QuantumInstance` with a :class:`.BackendV2` would have resulted in
an error.
Loading