diff --git a/qiskit/transpiler/passmanager_config.py b/qiskit/transpiler/passmanager_config.py index 4c53bc47b31c..0ebbff430301 100644 --- a/qiskit/transpiler/passmanager_config.py +++ b/qiskit/transpiler/passmanager_config.py @@ -13,6 +13,7 @@ """Pass Manager Configuration class.""" import pprint +import warnings from qiskit.transpiler.coupling import CouplingMap from qiskit.transpiler.instruction_durations import InstructionDurations @@ -113,11 +114,17 @@ def __init__( def from_backend(cls, backend, _skip_target=False, **pass_manager_options): """Construct a configuration based on a backend and user input. - This method automatically gererates a PassManagerConfig object based on the backend's + This method automatically generates a PassManagerConfig object based on the backend's features. User options can be used to overwrite the configuration. + .. deprecated:: 1.3 + The method ``PassManagerConfig.from_backend`` will stop supporting inputs of type + :class:`.BackendV1` in the `backend` parameter in a future release no + earlier than 2.0. :class:`.BackendV1` is deprecated and implementations should move + to :class:`.BackendV2`. + Args: - backend (BackendV1): The backend that provides the configuration. + backend (BackendV1 or BackendV2): The backend that provides the configuration. pass_manager_options: User-defined option-value pairs. Returns: @@ -126,12 +133,21 @@ def from_backend(cls, backend, _skip_target=False, **pass_manager_options): Raises: AttributeError: If the backend does not support a `configuration()` method. """ - res = cls(**pass_manager_options) backend_version = getattr(backend, "version", 0) + if backend_version == 1: + warnings.warn( + "The method PassManagerConfig.from_backend will stop supporting inputs of " + f"type `BackendV1` ( {backend} ) in the `backend` parameter in a future " + "release no earlier than 2.0. `BackendV1` is deprecated and implementations " + "should move to `BackendV2`.", + category=DeprecationWarning, + stacklevel=2, + ) if not isinstance(backend_version, int): backend_version = 0 if backend_version < 2: config = backend.configuration() + res = cls(**pass_manager_options) if res.basis_gates is None: if backend_version < 2: res.basis_gates = getattr(config, "basis_gates", None) diff --git a/releasenotes/notes/followup_12629-8bfcf1a3d4e6cabf.yaml b/releasenotes/notes/followup_12629-8bfcf1a3d4e6cabf.yaml new file mode 100644 index 000000000000..227946d0dffb --- /dev/null +++ b/releasenotes/notes/followup_12629-8bfcf1a3d4e6cabf.yaml @@ -0,0 +1,6 @@ +--- +deprecations_transpiler: + - | + The method :meth:`.PassManagerConfig.from_backend` will stop supporting inputs of type + :class:`.BackendV1` in the `backend` parameter in a future release no earlier than 2.0. + :class:`.BackendV1` is deprecated and implementations should move to :class:`.BackendV2`. diff --git a/test/python/primitives/test_backend_estimator_v2.py b/test/python/primitives/test_backend_estimator_v2.py index 12a932b83819..dd55750c55a0 100644 --- a/test/python/primitives/test_backend_estimator_v2.py +++ b/test/python/primitives/test_backend_estimator_v2.py @@ -145,7 +145,10 @@ def test_estimator_run_v1(self, backend, abelian_grouping): ): pm = generate_preset_pass_manager(optimization_level=0, backend=backend) psi1, psi2 = pm.run([psi1, psi2]) - estimator = BackendEstimatorV2(backend=backend, options=self._options) + with self.assertWarns(DeprecationWarning): + # When BackendEstimatorV2 is called with a backend V1, it raises a + # DeprecationWarning from PassManagerConfig.from_backend + estimator = BackendEstimatorV2(backend=backend, options=self._options) estimator.options.abelian_grouping = abelian_grouping # Specify the circuit and observable by indices. # calculate [ ] @@ -235,7 +238,10 @@ def test_estimator_with_pub_v1(self, backend, abelian_grouping): bind2 = BindingsArray.coerce({tuple(psi2.parameters): theta2}) pub2 = EstimatorPub(psi2, obs2, bind2) - estimator = BackendEstimatorV2(backend=backend, options=self._options) + with self.assertWarns(DeprecationWarning): + # When BackendEstimatorV2 is called with a backend V1, it raises a + # DeprecationWarning from PassManagerConfig.from_backend + estimator = BackendEstimatorV2(backend=backend, options=self._options) estimator.options.abelian_grouping = abelian_grouping result4 = estimator.run([pub1, pub2]).result() np.testing.assert_allclose(result4[0].data.evs, [1.55555728, -1.08766318], rtol=self._rtol) @@ -264,7 +270,10 @@ def test_estimator_run_no_params_v1(self, backend, abelian_grouping): ): pm = generate_preset_pass_manager(optimization_level=0, backend=backend) circuit = pm.run(circuit) - est = BackendEstimatorV2(backend=backend, options=self._options) + with self.assertWarns(DeprecationWarning): + # When BackendEstimatorV2 is called with a backend V1, it raises a + # DeprecationWarning from PassManagerConfig.from_backend + est = BackendEstimatorV2(backend=backend, options=self._options) est.options.abelian_grouping = abelian_grouping observable = self.observable.apply_layout(circuit.layout) result = est.run([(circuit, observable)]).result() @@ -331,7 +340,13 @@ def test_run_single_circuit_observable(self, backend, abelian_grouping): @combine(backend=BACKENDS_V1, abelian_grouping=[True, False]) def test_run_single_circuit_observable_v1(self, backend, abelian_grouping): """Test for single circuit and single observable case.""" - est = BackendEstimatorV2(backend=backend, options=self._options) + with self.assertWarnsRegex( + DeprecationWarning, + expected_regex=r"The method PassManagerConfig\.from_backend will stop supporting inputs of " + "type `BackendV1`", + ): + # BackendEstimatorV2 wont allow BackendV1 + est = BackendEstimatorV2(backend=backend, options=self._options) est.options.abelian_grouping = abelian_grouping with self.assertWarnsRegex( DeprecationWarning, @@ -438,7 +453,11 @@ def test_run_1qubit_v1(self, backend, abelian_grouping): op = SparsePauliOp.from_list([("I", 1)]) op2 = SparsePauliOp.from_list([("Z", 1)]) - est = BackendEstimatorV2(backend=backend, options=self._options) + with self.assertWarns(DeprecationWarning): + # When BackendEstimatorV2 is called with a backend V1, it raises a + # DeprecationWarning from PassManagerConfig.from_backend + est = BackendEstimatorV2(backend=backend, options=self._options) + est.options.abelian_grouping = abelian_grouping op_1 = op.apply_layout(qc.layout) result = est.run([(qc, op_1)]).result() @@ -513,7 +532,10 @@ def test_run_2qubits_v1(self, backend, abelian_grouping): op2 = SparsePauliOp.from_list([("ZI", 1)]) op3 = SparsePauliOp.from_list([("IZ", 1)]) - est = BackendEstimatorV2(backend=backend, options=self._options) + with self.assertWarns(DeprecationWarning): + # When BackendEstimatorV2 is called with a backend V1, it raises a + # DeprecationWarning from PassManagerConfig.from_backend + est = BackendEstimatorV2(backend=backend, options=self._options) est.options.abelian_grouping = abelian_grouping op_1 = op.apply_layout(qc.layout) result = est.run([(qc, op_1)]).result() @@ -539,7 +561,47 @@ def test_run_2qubits_v1(self, backend, abelian_grouping): result = est.run([(qc2, op_6)]).result() np.testing.assert_allclose(result[0].data.evs, [-1], rtol=self._rtol) - @combine(backend=BACKENDS, abelian_grouping=[True, False]) + @combine(backend=BACKENDS_V1, abelian_grouping=[True, False]) + def test_run_errors_v1(self, backend, abelian_grouping): + """Test for errors. + To be removed once BackendV1 is removed.""" + qc = QuantumCircuit(1) + qc2 = QuantumCircuit(2) + + op = SparsePauliOp.from_list([("I", 1)]) + op2 = SparsePauliOp.from_list([("II", 1)]) + with self.assertWarns(DeprecationWarning): + # When BackendEstimatorV2 is called with a backend V1, it raises a + # DeprecationWarning from PassManagerConfig.from_backend + est = BackendEstimatorV2(backend=backend, options=self._options) + est.options.abelian_grouping = abelian_grouping + with self.assertRaises(ValueError): + est.run([(qc, op2)]).result() + with self.assertRaises(ValueError): + est.run([(qc, op, [[1e4]])]).result() + with self.assertRaises(ValueError): + est.run([(qc2, op2, [[1, 2]])]).result() + with self.assertRaises(ValueError): + est.run([(qc, [op, op2], [[1]])]).result() + with self.assertRaises(ValueError): + est.run([(qc, op)], precision=-1).result() + with self.assertRaises(ValueError): + est.run([(qc, 1j * op)], precision=0.1).result() + # precision == 0 + with self.assertRaises(ValueError): + est.run([(qc, op, None, 0)]).result() + with self.assertRaises(ValueError): + est.run([(qc, op)], precision=0).result() + # precision < 0 + with self.assertRaises(ValueError): + est.run([(qc, op, None, -1)]).result() + with self.assertRaises(ValueError): + est.run([(qc, op)], precision=-1).result() + with self.subTest("missing []"): + with self.assertRaisesRegex(ValueError, "An invalid Estimator pub-like was given"): + _ = est.run((qc, op)).result() + + @combine(backend=BACKENDS_V2, abelian_grouping=[True, False]) def test_run_errors(self, backend, abelian_grouping): """Test for errors""" qc = QuantumCircuit(1) @@ -624,7 +686,13 @@ def test_run_numpy_params_v1(self, backend, abelian_grouping): statevector_estimator = StatevectorEstimator(seed=123) target = statevector_estimator.run([(qc, op, params_list)]).result() - backend_estimator = BackendEstimatorV2(backend=backend, options=self._options) + with self.assertWarnsRegex( + DeprecationWarning, + expected_regex=r"The method PassManagerConfig\.from_backend will stop supporting inputs of " + "type `BackendV1`", + ): + # BackendEstimatorV2 wont allow BackendV1 + backend_estimator = BackendEstimatorV2(backend=backend, options=self._options) backend_estimator.options.abelian_grouping = abelian_grouping with self.subTest("ndarrary"): @@ -662,7 +730,10 @@ def test_precision(self, backend, abelian_grouping): @combine(backend=BACKENDS_V1, abelian_grouping=[True, False]) def test_precision_v1(self, backend, abelian_grouping): """Test for precision""" - estimator = BackendEstimatorV2(backend=backend, options=self._options) + with self.assertWarns(DeprecationWarning): + # When BackendEstimatorV2 is called with a backend V1, it raises a + # DeprecationWarning from PassManagerConfig.from_backend + estimator = BackendEstimatorV2(backend=backend, options=self._options) estimator.options.abelian_grouping = abelian_grouping with self.assertWarnsRegex( DeprecationWarning, @@ -705,7 +776,10 @@ def test_diff_precision(self, backend, abelian_grouping): @combine(backend=BACKENDS_V1, abelian_grouping=[True, False]) def test_diff_precision_v1(self, backend, abelian_grouping): """Test for running different precisions at once""" - estimator = BackendEstimatorV2(backend=backend, options=self._options) + with self.assertWarns(DeprecationWarning): + # When BackendEstimatorV2 is called with a backend V1, it raises a + # DeprecationWarning from PassManagerConfig.from_backend + estimator = BackendEstimatorV2(backend=backend, options=self._options) estimator.options.abelian_grouping = abelian_grouping with self.assertWarnsRegex( DeprecationWarning, @@ -792,7 +866,10 @@ def test_job_size_limit_backend_v1(self): op = SparsePauliOp.from_list([("IZ", 1), ("XI", 2), ("ZY", -1)]) k = 5 param_list = self._rng.random(qc.num_parameters).tolist() - estimator = BackendEstimatorV2(backend=backend) + with self.assertWarns(DeprecationWarning): + # When BackendEstimatorV2 is called with a backend V1, it raises a + # DeprecationWarning from PassManagerConfig.from_backend + estimator = BackendEstimatorV2(backend=backend) with patch.object(backend, "run") as run_mock: estimator.run([(qc, op, param_list)] * k).result() self.assertEqual(run_mock.call_count, 10) diff --git a/test/python/transpiler/test_passmanager_config.py b/test/python/transpiler/test_passmanager_config.py index 341da357d37a..569f67738a16 100644 --- a/test/python/transpiler/test_passmanager_config.py +++ b/test/python/transpiler/test_passmanager_config.py @@ -33,7 +33,7 @@ def test_config_from_backend(self): """ with self.assertWarns(DeprecationWarning): backend = Fake27QPulseV1() - config = PassManagerConfig.from_backend(backend) + config = PassManagerConfig.from_backend(backend) self.assertEqual(config.basis_gates, backend.configuration().basis_gates) self.assertEqual(config.inst_map, backend.defaults().instruction_schedule_map) self.assertEqual( @@ -66,9 +66,9 @@ def test_from_backend_and_user_v1(self): with self.assertWarns(DeprecationWarning): backend = Fake20QV1() - config = PassManagerConfig.from_backend( - backend, basis_gates=["user_gate"], initial_layout=initial_layout - ) + config = PassManagerConfig.from_backend( + backend, basis_gates=["user_gate"], initial_layout=initial_layout + ) self.assertEqual(config.basis_gates, ["user_gate"]) self.assertNotEqual(config.basis_gates, backend.configuration().basis_gates) self.assertIsNone(config.inst_map) @@ -107,7 +107,8 @@ def test_from_backendv1_inst_map_is_none(self): with self.assertWarns(DeprecationWarning): backend = Fake27QPulseV1() backend.defaults = lambda: None - config = PassManagerConfig.from_backend(backend) + with self.assertWarns(DeprecationWarning): + config = PassManagerConfig.from_backend(backend) self.assertIsInstance(config, PassManagerConfig) self.assertIsNone(config.inst_map)