From b6005165b7754543e5302f336fee4a49370c0379 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Mon, 24 Jan 2022 13:11:37 -0500 Subject: [PATCH 01/10] Fix QuantumInstance compatibility with BackendV2 This commit fixes compatibility with the QuantumInstance class when targetting a BackendV2 backend. When BackendV2 was introduced testing with the QuantumInstance (and therefore all of qiskit.algorithms) was neglected and there are several compatibility issues with the class around hard coded assumptions that the backend being wrapped was a BaseBackend or BackendV1 object. --- qiskit/providers/backend.py | 9 ++ qiskit/test/mock/fake_backend_v2.py | 41 +++++ qiskit/utils/backend_utils.py | 31 +++- qiskit/utils/quantum_instance.py | 46 ++++-- qiskit/utils/run_circuits.py | 29 +++- ...-instance-backend-v2-a4e2678fe3ce39d1.yaml | 12 ++ test/python/algorithms/test_backendv2.py | 142 ++++++++++++++++++ 7 files changed, 286 insertions(+), 24 deletions(-) create mode 100644 releasenotes/notes/fix-quantum-instance-backend-v2-a4e2678fe3ce39d1.yaml create mode 100644 test/python/algorithms/test_backendv2.py diff --git a/qiskit/providers/backend.py b/qiskit/providers/backend.py index 51b0da959157..a706c2e62666 100644 --- a/qiskit/providers/backend.py +++ b/qiskit/providers/backend.py @@ -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. diff --git a/qiskit/test/mock/fake_backend_v2.py b/qiskit/test/mock/fake_backend_v2.py index e46683e90e24..31b8698adcf2 100644 --- a/qiskit/test/mock/fake_backend_v2.py +++ b/qiskit/test/mock/fake_backend_v2.py @@ -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", + ) + 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) diff --git a/qiskit/utils/backend_utils.py b/qiskit/utils/backend_utils.py index c377b6aa5f3c..2569cc4ddbd0 100644 --- a/qiskit/utils/backend_utils.py +++ b/qiskit/utils/backend_utils.py @@ -34,6 +34,23 @@ def __init__(self) -> None: _PROVIDER_CHECK = ProviderCheck() +def _get_backend_version(backend): + """Get the backend version int.""" + backend_version = getattr(backend, "version", None) + if not isinstance(backend_version, int): + backend_version = 0 + return backend_version + + +def _get_backend_provider(backend): + backend_version = _get_backend_version(backend) + if backend_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 +95,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 +114,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 +128,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 +161,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_version = _get_backend_version(backend) + if backend_version <= 1: + return backend.name().startswith("statevector") + else: + return backend.name.startswith("statevector") def is_simulator_backend(backend): diff --git a/qiskit/utils/quantum_instance.py b/qiskit/utils/quantum_instance.py index 37abf37ab031..5900707042e4 100644 --- a/qiskit/utils/quantum_instance.py +++ b/qiskit/utils/quantum_instance.py @@ -32,6 +32,7 @@ is_aer_qasm, is_basicaer_provider, support_backend_options, + _get_backend_provider, ) from qiskit.utils.mitigation import ( CompleteMeasFitter, @@ -239,15 +240,18 @@ def __init__( QiskitError: set backend_options but the backend does not support that """ self._backend = backend + self._backend_version = getattr(self._backend, "version", None) + if not isinstance(self._backend_version, int): + self._backend_version = 0 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 +284,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_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 +313,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 +381,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,9 +512,9 @@ 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) + 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 +843,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 +860,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,22 +974,31 @@ def backend(self): @property def backend_name(self): """Return backend name.""" - return self._backend.name() + if self._backend_version <= 1: + return self._backend.name() + else: + return self._backend.name @property def is_statevector(self): """Return True if backend is a statevector-type simulator.""" - return is_statevector_backend(self._backend) + if self._backend_version <= 1: + return is_statevector_backend(self._backend) + return False @property def is_simulator(self): """Return True if backend is a simulator.""" - return is_simulator_backend(self._backend) + if self._backend_version <= 1: + return is_simulator_backend(self._backend) + return False @property def is_local(self): """Return True if backend is a local backend.""" - return is_local_backend(self._backend) + if self._backend_version <= 1: + return is_local_backend(self._backend) + return False @property def skip_qobj_validation(self): diff --git a/qiskit/utils/run_circuits.py b/qiskit/utils/run_circuits.py index fccc1b6ab1a9..c94b3cb61ab9 100644 --- a/qiskit/utils/run_circuits.py +++ b/qiskit/utils/run_circuits.py @@ -277,7 +277,13 @@ def run_qobj( if is_local_backend(backend): max_circuits_per_job = sys.maxsize else: - max_circuits_per_job = backend.configuration().max_experiments + backend_version = getattr(backend, "version", None) + if not isinstance(backend_version, int): + backend_version = 0 + if backend_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 @@ -470,18 +476,31 @@ def run_circuits( Raises: QiskitError: Any error except for JobError raised by Qiskit Terra """ + backend_version = getattr(backend, "version", None) + if not isinstance(backend_version, int): + backend_version = 0 + backend_options = backend_options or {} noise_config = noise_config or {} run_config = run_config or {} - with_autorecover = not is_simulator_backend(backend) + if backend_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_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 = [] diff --git a/releasenotes/notes/fix-quantum-instance-backend-v2-a4e2678fe3ce39d1.yaml b/releasenotes/notes/fix-quantum-instance-backend-v2-a4e2678fe3ce39d1.yaml new file mode 100644 index 000000000000..c7cf64238c6e --- /dev/null +++ b/releasenotes/notes/fix-quantum-instance-backend-v2-a4e2678fe3ce39d1.yaml @@ -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. diff --git a/test/python/algorithms/test_backendv2.py b/test/python/algorithms/test_backendv2.py new file mode 100644 index 000000000000..3dd91cb0061c --- /dev/null +++ b/test/python/algorithms/test_backendv2.py @@ -0,0 +1,142 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. + +""" Test Providers that support BackendV2 interface """ + +import unittest +from test.python.algorithms import QiskitAlgorithmsTestCase +from qiskit import QuantumCircuit +from qiskit.test.mock import FakeProvider +from qiskit.test.mock.fake_backend_v2 import FakeBackendSimple +from qiskit.utils import QuantumInstance, algorithm_globals +from qiskit.algorithms import Shor, VQE, Grover, AmplificationProblem +from qiskit.opflow import X, Z, I +from qiskit.algorithms.optimizers import SPSA +from qiskit.circuit.library import TwoLocal, EfficientSU2 +from qiskit.utils.mitigation import CompleteMeasFitter + + +class TestBackendV2(QiskitAlgorithmsTestCase): + """test BackendV1 interface""" + + def setUp(self): + super().setUp() + self._provider = FakeProvider() + self._qasm = FakeBackendSimple() + self.seed = 50 + + def test_shor_factoring(self): + """shor factoring test""" + n_v = 15 + factors = [3, 5] + qasm_simulator = QuantumInstance( + self._qasm, shots=1000, seed_simulator=self.seed, seed_transpiler=self.seed + ) + shor = Shor(quantum_instance=qasm_simulator) + result = shor.factor(N=n_v) + self.assertListEqual(result.factors[0], factors) + self.assertTrue(result.total_counts >= result.successful_counts) + + def test_vqe_qasm(self): + """Test the VQE on QASM simulator.""" + h2_op = ( + -1.052373245772859 * (I ^ I) + + 0.39793742484318045 * (I ^ Z) + - 0.39793742484318045 * (Z ^ I) + - 0.01128010425623538 * (Z ^ Z) + + 0.18093119978423156 * (X ^ X) + ) + optimizer = SPSA(maxiter=300, last_avg=5) + wavefunction = TwoLocal(rotation_blocks="ry", entanglement_blocks="cz") + qasm_simulator = QuantumInstance( + self._qasm, shots=1024, seed_simulator=self.seed, seed_transpiler=self.seed + ) + vqe = VQE( + ansatz=wavefunction, + optimizer=optimizer, + max_evals_grouped=1, + quantum_instance=qasm_simulator, + ) + + result = vqe.compute_minimum_eigenvalue(operator=h2_op) + self.assertAlmostEqual(result.eigenvalue.real, -1.86, delta=0.05) + + def test_run_circuit_oracle(self): + """Test execution with a quantum circuit oracle""" + oracle = QuantumCircuit(2) + oracle.cz(0, 1) + problem = AmplificationProblem(oracle, is_good_state=["11"]) + qi = QuantumInstance( + self._provider.get_backend("fake_yorktown"), seed_simulator=12, seed_transpiler=32 + ) + grover = Grover(quantum_instance=qi) + result = grover.amplify(problem) + self.assertIn(result.top_measurement, ["11"]) + + def test_run_circuit_oracle_single_experiment_backend(self): + """Test execution with a quantum circuit oracle""" + oracle = QuantumCircuit(2) + oracle.cz(0, 1) + problem = AmplificationProblem(oracle, is_good_state=["11"]) + backend = self._provider.get_backend("fake_yorktown") + backend._configuration.max_experiments = 1 + qi = QuantumInstance( + self._provider.get_backend("fake_yorktown"), seed_simulator=12, seed_transpiler=32 + ) + grover = Grover(quantum_instance=qi) + result = grover.amplify(problem) + self.assertIn(result.top_measurement, ["11"]) + + def test_measurement_error_mitigation_with_vqe(self): + """measurement error mitigation test with vqe""" + try: + from qiskit.providers.aer import noise + except ImportError as ex: + self.skipTest(f"Package doesn't appear to be installed. Error: '{str(ex)}'") + return + + algorithm_globals.random_seed = 0 + + # build noise model + noise_model = noise.NoiseModel() + read_err = noise.errors.readout_error.ReadoutError([[0.9, 0.1], [0.25, 0.75]]) + noise_model.add_all_qubit_readout_error(read_err) + + backend = self._qasm + + quantum_instance = QuantumInstance( + backend=backend, + seed_simulator=167, + seed_transpiler=167, + noise_model=noise_model, + measurement_error_mitigation_cls=CompleteMeasFitter, + ) + + h2_hamiltonian = ( + -1.052373245772859 * (I ^ I) + + 0.39793742484318045 * (I ^ Z) + - 0.39793742484318045 * (Z ^ I) + - 0.01128010425623538 * (Z ^ Z) + + 0.18093119978423156 * (X ^ X) + ) + optimizer = SPSA(maxiter=200) + ansatz = EfficientSU2(2, reps=1) + + vqe = VQE(ansatz=ansatz, optimizer=optimizer, quantum_instance=quantum_instance) + result = vqe.compute_minimum_eigenvalue(operator=h2_hamiltonian) + self.assertGreater(quantum_instance.time_taken, 0.0) + quantum_instance.reset_execution_results() + self.assertAlmostEqual(result.eigenvalue.real, -1.86, delta=0.05) + + +if __name__ == "__main__": + unittest.main() From 3e3dd5365a115271f5903b457650efe067e80a34 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Mon, 24 Jan 2022 14:46:45 -0500 Subject: [PATCH 02/10] Remove invalid test The tests for the QuantumInstance with BackendV2 were just ported over from the equivalent testing with BackendV1. However, one of those tests was to test error mitigation with a custom noise model which requires qiskit-aer. We can't test this without aer having been migrated to BackendV2. Until qiskit-aer is backendv2 enabled this commit just removes the test. --- qiskit/utils/backend_utils.py | 10 ++++-- test/python/algorithms/test_backendv2.py | 41 ------------------------ 2 files changed, 8 insertions(+), 43 deletions(-) diff --git a/qiskit/utils/backend_utils.py b/qiskit/utils/backend_utils.py index 2569cc4ddbd0..8d419d9d9b2a 100644 --- a/qiskit/utils/backend_utils.py +++ b/qiskit/utils/backend_utils.py @@ -179,7 +179,10 @@ def is_simulator_backend(backend): Returns: bool: True is a simulator """ - return backend.configuration().simulator + backend_version = _get_backend_version(backend) + if backend_version <= 1: + return backend.configuration().simulator + return False def is_local_backend(backend): @@ -191,7 +194,10 @@ def is_local_backend(backend): Returns: bool: True is a local backend """ - return backend.configuration().local + backend_version = _get_backend_version(backend) + if backend_version <= 1: + return backend.configuration().local + return False def is_aer_qasm(backend): diff --git a/test/python/algorithms/test_backendv2.py b/test/python/algorithms/test_backendv2.py index 3dd91cb0061c..3a9abe347651 100644 --- a/test/python/algorithms/test_backendv2.py +++ b/test/python/algorithms/test_backendv2.py @@ -96,47 +96,6 @@ def test_run_circuit_oracle_single_experiment_backend(self): result = grover.amplify(problem) self.assertIn(result.top_measurement, ["11"]) - def test_measurement_error_mitigation_with_vqe(self): - """measurement error mitigation test with vqe""" - try: - from qiskit.providers.aer import noise - except ImportError as ex: - self.skipTest(f"Package doesn't appear to be installed. Error: '{str(ex)}'") - return - - algorithm_globals.random_seed = 0 - - # build noise model - noise_model = noise.NoiseModel() - read_err = noise.errors.readout_error.ReadoutError([[0.9, 0.1], [0.25, 0.75]]) - noise_model.add_all_qubit_readout_error(read_err) - - backend = self._qasm - - quantum_instance = QuantumInstance( - backend=backend, - seed_simulator=167, - seed_transpiler=167, - noise_model=noise_model, - measurement_error_mitigation_cls=CompleteMeasFitter, - ) - - h2_hamiltonian = ( - -1.052373245772859 * (I ^ I) - + 0.39793742484318045 * (I ^ Z) - - 0.39793742484318045 * (Z ^ I) - - 0.01128010425623538 * (Z ^ Z) - + 0.18093119978423156 * (X ^ X) - ) - optimizer = SPSA(maxiter=200) - ansatz = EfficientSU2(2, reps=1) - - vqe = VQE(ansatz=ansatz, optimizer=optimizer, quantum_instance=quantum_instance) - result = vqe.compute_minimum_eigenvalue(operator=h2_hamiltonian) - self.assertGreater(quantum_instance.time_taken, 0.0) - quantum_instance.reset_execution_results() - self.assertAlmostEqual(result.eigenvalue.real, -1.86, delta=0.05) - if __name__ == "__main__": unittest.main() From de32972d00c9588a45c290349517bc67dbd61b08 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Mon, 24 Jan 2022 16:45:25 -0500 Subject: [PATCH 03/10] Remove unused imports --- test/python/algorithms/test_backendv2.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/python/algorithms/test_backendv2.py b/test/python/algorithms/test_backendv2.py index 3a9abe347651..e87d01704379 100644 --- a/test/python/algorithms/test_backendv2.py +++ b/test/python/algorithms/test_backendv2.py @@ -17,12 +17,11 @@ from qiskit import QuantumCircuit from qiskit.test.mock import FakeProvider from qiskit.test.mock.fake_backend_v2 import FakeBackendSimple -from qiskit.utils import QuantumInstance, algorithm_globals +from qiskit.utils import QuantumInstance from qiskit.algorithms import Shor, VQE, Grover, AmplificationProblem from qiskit.opflow import X, Z, I from qiskit.algorithms.optimizers import SPSA -from qiskit.circuit.library import TwoLocal, EfficientSU2 -from qiskit.utils.mitigation import CompleteMeasFitter +from qiskit.circuit.library import TwoLocal class TestBackendV2(QiskitAlgorithmsTestCase): From bfdf394f4e4e3fdf9314f6b402dbdccae238b656 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 26 Jan 2022 09:12:20 -0500 Subject: [PATCH 04/10] Remove duplicate backend version checks from QuantumInstance The QuantumInstance's is_simulator() and is_local() methods had duplicate checking on the backend version. This was an artifact of testing where in earlier revisions of this PR branch it was done in the QuantumInstance before being moved to the inner helper functions. Since the helper functions are updated to handle the version checking now we no longer need it in the QuantumInstance methods too. This commit removes this and just relies on the helper functions to do the version checking. --- qiskit/utils/quantum_instance.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/qiskit/utils/quantum_instance.py b/qiskit/utils/quantum_instance.py index 5900707042e4..d3834bba89d2 100644 --- a/qiskit/utils/quantum_instance.py +++ b/qiskit/utils/quantum_instance.py @@ -989,16 +989,12 @@ def is_statevector(self): @property def is_simulator(self): """Return True if backend is a simulator.""" - if self._backend_version <= 1: - return is_simulator_backend(self._backend) - return False + return is_simulator_backend(self._backend) @property def is_local(self): """Return True if backend is a local backend.""" - if self._backend_version <= 1: - return is_local_backend(self._backend) - return False + return is_local_backend(self._backend) @property def skip_qobj_validation(self): From bfd981a261639e2545d2f5524c369e005e8121f8 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 26 Jan 2022 09:43:20 -0500 Subject: [PATCH 05/10] Use more descriptive local variable name for backend version This commit attempts to clarify how the version fields are used by naming the local variables backend_interface_version to make it clear what we're checking in that context. --- qiskit/utils/backend_utils.py | 24 ++++++++++++------------ qiskit/utils/quantum_instance.py | 12 ++++++------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/qiskit/utils/backend_utils.py b/qiskit/utils/backend_utils.py index 8d419d9d9b2a..38b0793a5957 100644 --- a/qiskit/utils/backend_utils.py +++ b/qiskit/utils/backend_utils.py @@ -36,15 +36,15 @@ def __init__(self) -> None: def _get_backend_version(backend): """Get the backend version int.""" - backend_version = getattr(backend, "version", None) - if not isinstance(backend_version, int): - backend_version = 0 - return backend_version + backend_interface_version = getattr(backend, "version", None) + if not isinstance(backend_interface_version, int): + backend_interface_version = 0 + return backend_interface_version def _get_backend_provider(backend): - backend_version = _get_backend_version(backend) - if backend_version > 1: + backend_interface_version = _get_backend_version(backend) + if backend_interface_version > 1: provider = backend.provider else: provider = backend.provider() @@ -163,8 +163,8 @@ def is_statevector_backend(backend): return True if backend is None: return False - backend_version = _get_backend_version(backend) - if backend_version <= 1: + backend_interface_version = _get_backend_version(backend) + if backend_interface_version <= 1: return backend.name().startswith("statevector") else: return backend.name.startswith("statevector") @@ -179,8 +179,8 @@ def is_simulator_backend(backend): Returns: bool: True is a simulator """ - backend_version = _get_backend_version(backend) - if backend_version <= 1: + backend_interface_version = _get_backend_version(backend) + if backend_interface_version <= 1: return backend.configuration().simulator return False @@ -194,8 +194,8 @@ def is_local_backend(backend): Returns: bool: True is a local backend """ - backend_version = _get_backend_version(backend) - if backend_version <= 1: + backend_interface_version = _get_backend_version(backend) + if backend_interface_version <= 1: return backend.configuration().local return False diff --git a/qiskit/utils/quantum_instance.py b/qiskit/utils/quantum_instance.py index d3834bba89d2..569a076e867b 100644 --- a/qiskit/utils/quantum_instance.py +++ b/qiskit/utils/quantum_instance.py @@ -240,9 +240,9 @@ def __init__( QiskitError: set backend_options but the backend does not support that """ self._backend = backend - self._backend_version = getattr(self._backend, "version", None) - if not isinstance(self._backend_version, int): - self._backend_version = 0 + self._backend_interface_version = getattr(self._backend, "version", None) + if not isinstance(self._backend_interface_version, int): + self._backend_interface_version = 0 self._pass_manager = pass_manager self._bound_pass_manager = bound_pass_manager @@ -284,7 +284,7 @@ def __init__( self._run_config = run_config # setup backend config - if self._backend_version <= 1: + 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} @@ -974,7 +974,7 @@ def backend(self): @property def backend_name(self): """Return backend name.""" - if self._backend_version <= 1: + if self._backend_interface_version <= 1: return self._backend.name() else: return self._backend.name @@ -982,7 +982,7 @@ def backend_name(self): @property def is_statevector(self): """Return True if backend is a statevector-type simulator.""" - if self._backend_version <= 1: + if self._backend_interface_version <= 1: return is_statevector_backend(self._backend) return False From 09d60907576fddc798ee7897513d90d8ed1852f3 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 26 Jan 2022 09:51:43 -0500 Subject: [PATCH 06/10] Fix handling of statevector simulator check for BackendV2 This commit fixes the check for whether we're running with a statevector simulator (to determine whether we need to insert save_statevector instructions) to be BackendV2 compatible. This isn't strictly needed yet as qiskit-aer is still based on BackendV1, but this would cause a bug when it does switch to the newer backend version. This commit just gets in front of that potential issue so we don't have a bug in the future. --- qiskit/utils/quantum_instance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/utils/quantum_instance.py b/qiskit/utils/quantum_instance.py index 569a076e867b..17d78d2544db 100644 --- a/qiskit/utils/quantum_instance.py +++ b/qiskit/utils/quantum_instance.py @@ -515,7 +515,7 @@ def execute(self, circuits, had_transpiled: bool = False): from qiskit.providers import Backend circuit_job = isinstance(self._backend, Backend) - if self.is_statevector and self._backend.name() == "aer_simulator_statevector": + if self.is_statevector and self.backend_name == "aer_simulator_statevector": try: from qiskit.providers.aer.library import SaveStatevector From 363e8dd699bd099f54b18201c1217424e5383be8 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 27 Jan 2022 15:39:16 -0500 Subject: [PATCH 07/10] Use backend_interface_version everywhere There were a few straglers still using backend_version instead of backend_interface_version in the code. This commit updates all these instances so there is consistent naming around this inside the utils subpackage. --- qiskit/utils/backend_utils.py | 12 +++++++----- qiskit/utils/quantum_instance.py | 5 ++--- qiskit/utils/run_circuits.py | 11 ++++------- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/qiskit/utils/backend_utils.py b/qiskit/utils/backend_utils.py index 38b0793a5957..219e9c9a88fb 100644 --- a/qiskit/utils/backend_utils.py +++ b/qiskit/utils/backend_utils.py @@ -34,16 +34,18 @@ def __init__(self) -> None: _PROVIDER_CHECK = ProviderCheck() -def _get_backend_version(backend): +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_version(backend) + backend_interface_version = _get_backend_interface_version(backend) if backend_interface_version > 1: provider = backend.provider else: @@ -163,7 +165,7 @@ def is_statevector_backend(backend): return True if backend is None: return False - backend_interface_version = _get_backend_version(backend) + backend_interface_version = _get_backend_interface_version(backend) if backend_interface_version <= 1: return backend.name().startswith("statevector") else: @@ -179,7 +181,7 @@ def is_simulator_backend(backend): Returns: bool: True is a simulator """ - backend_interface_version = _get_backend_version(backend) + backend_interface_version = _get_backend_interface_version(backend) if backend_interface_version <= 1: return backend.configuration().simulator return False @@ -194,7 +196,7 @@ def is_local_backend(backend): Returns: bool: True is a local backend """ - backend_interface_version = _get_backend_version(backend) + backend_interface_version = _get_backend_interface_version(backend) if backend_interface_version <= 1: return backend.configuration().local return False diff --git a/qiskit/utils/quantum_instance.py b/qiskit/utils/quantum_instance.py index 17d78d2544db..815c1919f063 100644 --- a/qiskit/utils/quantum_instance.py +++ b/qiskit/utils/quantum_instance.py @@ -33,6 +33,7 @@ is_basicaer_provider, support_backend_options, _get_backend_provider, + _get_backend_interface_version ) from qiskit.utils.mitigation import ( CompleteMeasFitter, @@ -240,9 +241,7 @@ def __init__( QiskitError: set backend_options but the backend does not support that """ self._backend = backend - self._backend_interface_version = getattr(self._backend, "version", None) - if not isinstance(self._backend_interface_version, int): - self._backend_interface_version = 0 + self._backend_interface_version = _get_backend_interface_version(self._backend) self._pass_manager = pass_manager self._bound_pass_manager = bound_pass_manager diff --git a/qiskit/utils/run_circuits.py b/qiskit/utils/run_circuits.py index c94b3cb61ab9..8424afab90dc 100644 --- a/qiskit/utils/run_circuits.py +++ b/qiskit/utils/run_circuits.py @@ -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) @@ -277,10 +278,8 @@ def run_qobj( if is_local_backend(backend): max_circuits_per_job = sys.maxsize else: - backend_version = getattr(backend, "version", None) - if not isinstance(backend_version, int): - backend_version = 0 - if backend_version <= 1: + 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 @@ -476,9 +475,7 @@ def run_circuits( Raises: QiskitError: Any error except for JobError raised by Qiskit Terra """ - backend_version = getattr(backend, "version", None) - if not isinstance(backend_version, int): - backend_version = 0 + backend_interface_version = _get_backend_interface_version(backend) backend_options = backend_options or {} noise_config = noise_config or {} From 1bca4dbced0d8ff2b71722a243c93d32b134d3e4 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 27 Jan 2022 15:40:18 -0500 Subject: [PATCH 08/10] Fix handling of statevector simulator detection This commit fixes an oversight from an earlier development version that slipped through later revisions where the statevector simulator backend detection in the QuantumInstance class was hard coded to False for BackendV2. This was just meant as a temporary debugging step while getting the initial commit ready, but was never updated to remove this until now. --- qiskit/utils/quantum_instance.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/qiskit/utils/quantum_instance.py b/qiskit/utils/quantum_instance.py index 815c1919f063..aace2c8e4941 100644 --- a/qiskit/utils/quantum_instance.py +++ b/qiskit/utils/quantum_instance.py @@ -981,9 +981,7 @@ def backend_name(self): @property def is_statevector(self): """Return True if backend is a statevector-type simulator.""" - if self._backend_interface_version <= 1: - return is_statevector_backend(self._backend) - return False + return is_statevector_backend(self._backend) @property def is_simulator(self): From afecf0a20c2c5c9e0b6fab0f29156260216d0626 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 27 Jan 2022 16:00:23 -0500 Subject: [PATCH 09/10] Actually commit local variable backend_interface_version renames --- qiskit/utils/quantum_instance.py | 2 +- qiskit/utils/run_circuits.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/qiskit/utils/quantum_instance.py b/qiskit/utils/quantum_instance.py index aace2c8e4941..ec7276e9ea83 100644 --- a/qiskit/utils/quantum_instance.py +++ b/qiskit/utils/quantum_instance.py @@ -33,7 +33,7 @@ is_basicaer_provider, support_backend_options, _get_backend_provider, - _get_backend_interface_version + _get_backend_interface_version, ) from qiskit.utils.mitigation import ( CompleteMeasFitter, diff --git a/qiskit/utils/run_circuits.py b/qiskit/utils/run_circuits.py index 8424afab90dc..802229131b7e 100644 --- a/qiskit/utils/run_circuits.py +++ b/qiskit/utils/run_circuits.py @@ -33,7 +33,7 @@ is_simulator_backend, is_local_backend, is_ibmq_provider, - _get_backend_interface_version + _get_backend_interface_version, ) MAX_CIRCUITS_PER_JOB = os.environ.get("QISKIT_AQUA_MAX_CIRCUITS_PER_JOB", None) @@ -480,7 +480,7 @@ def run_circuits( backend_options = backend_options or {} noise_config = noise_config or {} run_config = run_config or {} - if backend_version <= 1: + if backend_interface_version <= 1: with_autorecover = not is_simulator_backend(backend) else: with_autorecover = False @@ -488,7 +488,7 @@ def run_circuits( if MAX_CIRCUITS_PER_JOB is not None: max_circuits_per_job = int(MAX_CIRCUITS_PER_JOB) else: - if backend_version <= 1: + if backend_interface_version <= 1: if is_local_backend(backend): max_circuits_per_job = sys.maxsize else: From 8a0a922bb7666a21dee10ecec69298e99e0cf5a3 Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Tue, 1 Feb 2022 18:59:29 +0000 Subject: [PATCH 10/10] Fix typo --- test/python/algorithms/test_backendv2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/python/algorithms/test_backendv2.py b/test/python/algorithms/test_backendv2.py index e87d01704379..e96d5d2176bf 100644 --- a/test/python/algorithms/test_backendv2.py +++ b/test/python/algorithms/test_backendv2.py @@ -25,7 +25,7 @@ class TestBackendV2(QiskitAlgorithmsTestCase): - """test BackendV1 interface""" + """test BackendV2 interface""" def setUp(self): super().setUp()