-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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 #7563
Changes from 13 commits
b600516
3e3dd53
de32972
bfdf394
bfd981a
09d6090
c0f3735
c9a6cfe
363e8dd
1bca4db
afecf0a
3895703
8a0a922
18fa909
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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): | ||
|
@@ -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", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am a bit confused. There seem to be two different There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. It is explained pretty clearly in a docstring, but I forgot which and just now I was unable to locate it quickly. As I was digging through the code it became clear that this is what's happening even before I found the docstring. The semvar-ish version is for the particular backends, i.e. child classes. The integer version is for the parent classes of these. It would be nice to use a completely different word than "version" for one of these, to avoid confusion. But, @mtreinish pointed out that that would require a deprecation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you thinking of the But yeah, @jlapeyre is correct as to the difference. The |
||
) | ||
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) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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: | ||
|
@@ -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 | ||
|
||
|
@@ -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): | ||
|
@@ -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 | ||
|
||
|
@@ -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): | ||
|
@@ -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 | ||
Comment on lines
-159
to
+187
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well it's more that That being said the code that uses this in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Haha, let's just leave it as-is then. |
||
|
||
|
||
def is_local_backend(backend): | ||
|
@@ -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 | ||
Comment on lines
-171
to
+202
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Similar question to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is basically the same answer as That being said all this does in |
||
|
||
|
||
def is_aer_qasm(backend): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,6 +32,8 @@ | |
is_aer_qasm, | ||
is_basicaer_provider, | ||
support_backend_options, | ||
_get_backend_provider, | ||
_get_backend_interface_version, | ||
Comment on lines
+35
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we're importing them elsewhere, they kind of ought to be public. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's mostly so we don't have to backport a new public interface. We can promote it on main if we want |
||
) | ||
from qiskit.utils.mitigation import ( | ||
CompleteMeasFitter, | ||
|
@@ -239,15 +241,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: | ||
|
@@ -280,9 +283,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 = { | ||
|
@@ -306,7 +312,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 | ||
|
@@ -374,7 +380,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, | ||
|
@@ -505,10 +511,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 | ||
|
||
|
@@ -836,7 +842,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: | ||
|
@@ -853,7 +859,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 | ||
|
@@ -967,7 +973,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): | ||
|
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this class a temporary solution for while
FakeBackendV2
doesn't have.run
method implemented? What prevents us to implemented.run
method toFakeBackendV2
now? Is it blocked by #7391 that I am working on?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this is just to have something until #7391 is implemented. But also because the goal of this PR is to fix support I wanted to have something smaller to backport than what #7391 will end up being. To test the quantum instance works with BackendV2 there needs to be a backend implementing the v2 interface with
run()
defined. So I threw this together so we can have a minimal backend that does this.