diff --git a/qiskit/primitives/backend_estimator.py b/qiskit/primitives/backend_estimator.py index 89ace13006e5..2ffe5bb7a989 100644 --- a/qiskit/primitives/backend_estimator.py +++ b/qiskit/primitives/backend_estimator.py @@ -15,7 +15,6 @@ from __future__ import annotations -import copy import typing from collections.abc import Sequence from itertools import accumulate @@ -29,7 +28,14 @@ from qiskit.quantum_info import Pauli, PauliList from qiskit.quantum_info.operators.base_operator import BaseOperator from qiskit.result import Counts, Result -from qiskit.transpiler import PassManager +from qiskit.transpiler import CouplingMap, PassManager +from qiskit.transpiler.passes import ( + ApplyLayout, + EnlargeWithAncilla, + FullAncillaAllocation, + Optimize1qGatesDecomposition, + SetLayout, +) from .base import BaseEstimator, EstimatorResult from .primitive_job import PrimitiveJob @@ -210,9 +216,8 @@ def _transpile(self): perm_pattern = list(range(transpiled_circuit.num_qubits)) # 2. transpile diff circuits - transpile_opts = copy.copy(self.transpile_options) - transpile_opts.update_options(initial_layout=perm_pattern) - diff_circuits = transpile(diff_circuits, self.backend, **transpile_opts.__dict__) + passmanager = _passmanager_for_measurement_circuits(perm_pattern, self.backend) + diff_circuits = passmanager.run(diff_circuits) # 3. combine transpiled_circuits = [] for diff_circuit in diff_circuits: @@ -344,7 +349,7 @@ def _preprocessing(self) -> list[tuple[QuantumCircuit, list[QuantumCircuit]]]: } diff_circuits.append(meas_circuit) else: - for basis, obs in zip(observable.paulis, observable): # type: ignore + for basis, obs in zip(observable.paulis, observable): meas_circuit, indices = self._measurement_circuit(circuit.num_qubits, basis) paulis = PauliList.from_symplectic( obs.paulis.z[:, indices], @@ -456,3 +461,22 @@ def _pauli_expval_with_variance(counts: Counts, paulis: PauliList) -> tuple[np.n # Compute variance variances = 1 - expvals**2 return expvals, variances + + +def _passmanager_for_measurement_circuits(layout, backend) -> PassManager: + passmanager = PassManager([SetLayout(layout)]) + if isinstance(backend, BackendV2): + opt1q = Optimize1qGatesDecomposition(target=backend.target) + else: + opt1q = Optimize1qGatesDecomposition(basis=backend.configuration().basis_gates) + passmanager.append(opt1q) + if isinstance(backend, BackendV2) and isinstance(backend.coupling_map, CouplingMap): + coupling_map = backend.coupling_map + passmanager.append(FullAncillaAllocation(coupling_map)) + passmanager.append(EnlargeWithAncilla()) + elif isinstance(backend, BackendV1) and backend.configuration().coupling_map is not None: + coupling_map = CouplingMap(backend.configuration().coupling_map) + passmanager.append(FullAncillaAllocation(coupling_map)) + passmanager.append(EnlargeWithAncilla()) + passmanager.append(ApplyLayout()) + return passmanager diff --git a/releasenotes/notes/backend-estimator-v2-support-a698353aeeb5236c.yaml b/releasenotes/notes/backend-estimator-v2-support-a698353aeeb5236c.yaml new file mode 100644 index 000000000000..544adfa56170 --- /dev/null +++ b/releasenotes/notes/backend-estimator-v2-support-a698353aeeb5236c.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Fixed a bug where :class:`.primitives.BackendEstimator` cannot be used with the + :class:`.providers.BackendV2` that does not have coupling_map. diff --git a/test/python/primitives/test_backend_estimator.py b/test/python/primitives/test_backend_estimator.py index 6ba18fd23b4a..01f8c1fa251a 100644 --- a/test/python/primitives/test_backend_estimator.py +++ b/test/python/primitives/test_backend_estimator.py @@ -25,12 +25,13 @@ from qiskit.primitives import BackendEstimator, EstimatorResult from qiskit.providers import JobV1 from qiskit.providers.fake_provider import FakeNairobi, FakeNairobiV2 +from qiskit.providers.fake_provider.fake_backend_v2 import FakeBackendSimple from qiskit.quantum_info import SparsePauliOp from qiskit.test import QiskitTestCase from qiskit.transpiler import PassManager from qiskit.utils import optionals -BACKENDS = [FakeNairobi(), FakeNairobiV2()] +BACKENDS = [FakeNairobi(), FakeNairobiV2(), FakeBackendSimple()] @ddt @@ -359,7 +360,7 @@ def test_layout(self, backend): estimator = BackendEstimator(backend) estimator.set_transpile_options(seed_transpiler=15) value = estimator.run(qc, op, shots=10000).result().values[0] - if optionals.HAS_AER: + if optionals.HAS_AER and not isinstance(backend, FakeBackendSimple): self.assertEqual(value, -0.916) else: self.assertEqual(value, -1) @@ -374,7 +375,7 @@ def test_layout(self, backend): estimator = BackendEstimator(backend) estimator.set_transpile_options(initial_layout=[0, 1, 2], seed_transpiler=15) value = estimator.run(qc, op, shots=10000).result().values[0] - if optionals.HAS_AER: + if optionals.HAS_AER and not isinstance(backend, FakeBackendSimple): self.assertEqual(value, -0.8902) else: self.assertEqual(value, -1)