From 9dbf5d157aca743d7aecbe3dab74b4154ff8c259 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 20 Mar 2024 10:33:07 +0000 Subject: [PATCH] Add better error messages for typical SamplerV2 and EstimatorV2 error cases (backport #12031) (#12041) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add better error messages for typical SamplerV2 and EstimatorV2 error cases (#12031) * add an early error for a case * Update qiskit/primitives/containers/sampler_pub.py Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com> * add better message for estimator pub * Update qiskit/primitives/containers/estimator_pub.py Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com> * add an additional message --------- Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com> (cherry picked from commit 194cc8fb9aedc24a5082eff351fcb7b70da59f17) # Conflicts: # test/python/primitives/test_backend_estimator_v2.py # test/python/primitives/test_backend_sampler_v2.py * remove tests for backend primitives V2 because they will appear for 1.1 --------- Co-authored-by: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com> Co-authored-by: Takashi Imamichi --- qiskit/primitives/containers/estimator_pub.py | 13 ++++++++--- qiskit/primitives/containers/sampler_pub.py | 22 ++++++++++++++++--- .../primitives/test_backend_estimator.py | 9 ++++---- .../python/primitives/test_backend_sampler.py | 3 ++- test/python/primitives/test_estimator.py | 3 ++- test/python/primitives/test_sampler.py | 3 ++- .../primitives/test_statevector_estimator.py | 6 ++++- .../primitives/test_statevector_sampler.py | 6 +++++ 8 files changed, 51 insertions(+), 14 deletions(-) diff --git a/qiskit/primitives/containers/estimator_pub.py b/qiskit/primitives/containers/estimator_pub.py index fed278ae14ae..af31327cb6b8 100644 --- a/qiskit/primitives/containers/estimator_pub.py +++ b/qiskit/primitives/containers/estimator_pub.py @@ -132,6 +132,15 @@ def coerce(cls, pub: EstimatorPubLike, precision: float | None = None) -> Estima validate=False, # Assume Pub is already validated ) return pub + + if isinstance(pub, QuantumCircuit): + raise ValueError( + f"An invalid Estimator pub-like was given ({type(pub)}). " + "If you want to run a single pub, you need to wrap it with `[]` like " + "`estimator.run([(circuit, observables, param_values)])` " + "instead of `estimator.run((circuit, observables, param_values))`." + ) + if len(pub) not in [2, 3, 4]: raise ValueError( f"The length of pub must be 2, 3 or 4, but length {len(pub)} is given." @@ -208,8 +217,6 @@ def validate(self): An Estimator Pub can also be initialized in the following formats which will be converted to the full Pub tuple: - * ``circuit - * ``(circuit,)`` * ``(circuit, observables)`` - * ``(circuit, observalbes, parameter_values)`` + * ``(circuit, observables, parameter_values)`` """ diff --git a/qiskit/primitives/containers/sampler_pub.py b/qiskit/primitives/containers/sampler_pub.py index 9828a74c5013..d79d8b40e97b 100644 --- a/qiskit/primitives/containers/sampler_pub.py +++ b/qiskit/primitives/containers/sampler_pub.py @@ -18,10 +18,11 @@ from __future__ import annotations from collections.abc import Mapping -from typing import Tuple, Union from numbers import Integral +from typing import Tuple, Union from qiskit import QuantumCircuit +from qiskit.circuit import CircuitInstruction from .bindings_array import BindingsArray, BindingsArrayLike from .shape import ShapedMixin @@ -113,6 +114,14 @@ def coerce(cls, pub: SamplerPubLike, shots: int | None = None) -> SamplerPub: if isinstance(pub, QuantumCircuit): return cls(circuit=pub, shots=shots, validate=True) + if isinstance(pub, CircuitInstruction): + raise ValueError( + f"An invalid Sampler pub-like was given ({type(pub)}). " + "If you want to run a single circuit, " + "you need to wrap it with `[]` like `sampler.run([circuit])` " + "instead of `sampler.run(circuit)`." + ) + if len(pub) not in [1, 2, 3]: raise ValueError( f"The length of pub must be 1, 2 or 3, but length {len(pub)} is given." @@ -147,10 +156,17 @@ def validate(self): # Cross validate circuits and parameter values num_parameters = self.parameter_values.num_parameters if num_parameters != self.circuit.num_parameters: - raise ValueError( + message = ( f"The number of values ({num_parameters}) does not match " f"the number of parameters ({self.circuit.num_parameters}) for the circuit." ) + if num_parameters == 0: + message += ( + " Note that if you want to run a single pub, you need to wrap it with `[]` like " + "`sampler.run([(circuit, param_values)])` instead of " + "`sampler.run((circuit, param_values))`." + ) + raise ValueError(message) SamplerPubLike = Union[ @@ -171,7 +187,7 @@ def validate(self): A Sampler Pub can also be initialized in the following formats which will be converted to the full Pub tuple: - * ``circuit + * ``circuit`` * ``(circuit,)`` * ``(circuit, parameter_values)`` """ diff --git a/test/python/primitives/test_backend_estimator.py b/test/python/primitives/test_backend_estimator.py index 6e14d7e4c2db..fd55b24aa761 100644 --- a/test/python/primitives/test_backend_estimator.py +++ b/test/python/primitives/test_backend_estimator.py @@ -56,6 +56,7 @@ class TestBackendEstimator(QiskitTestCase): def setUp(self): super().setUp() + self._rng = np.random.default_rng(12) self.ansatz = RealAmplitudes(num_qubits=2, reps=2) self.observable = SparsePauliOp.from_list( [ @@ -222,7 +223,7 @@ def test_run_numpy_params(self, backend): qc = RealAmplitudes(num_qubits=2, reps=2) op = SparsePauliOp.from_list([("IZ", 1), ("XI", 2), ("ZY", -1)]) k = 5 - params_array = np.random.rand(k, qc.num_parameters) + params_array = self._rng.random((k, qc.num_parameters)) params_list = params_array.tolist() params_list_array = list(params_array) estimator = BackendEstimator(backend=backend) @@ -288,7 +289,7 @@ def max_circuits(self): qc = RealAmplitudes(num_qubits=2, reps=2) op = SparsePauliOp.from_list([("IZ", 1), ("XI", 2), ("ZY", -1)]) k = 5 - params_array = np.random.rand(k, qc.num_parameters) + params_array = self._rng.random((k, qc.num_parameters)) params_list = params_array.tolist() estimator = BackendEstimator(backend=backend) with patch.object(backend, "run") as run_mock: @@ -304,7 +305,7 @@ def test_job_size_limit_v1(self): qc = RealAmplitudes(num_qubits=2, reps=2) op = SparsePauliOp.from_list([("IZ", 1), ("XI", 2), ("ZY", -1)]) k = 5 - params_array = np.random.rand(k, qc.num_parameters) + params_array = self._rng.random((k, qc.num_parameters)) params_list = params_array.tolist() estimator = BackendEstimator(backend=backend) estimator.set_options(seed_simulator=123) @@ -321,7 +322,7 @@ def test_no_max_circuits(self): qc = RealAmplitudes(num_qubits=2, reps=2) op = SparsePauliOp.from_list([("IZ", 1), ("XI", 2), ("ZY", -1)]) k = 5 - params_array = np.random.rand(k, qc.num_parameters) + params_array = self._rng.random((k, qc.num_parameters)) params_list = params_array.tolist() params_list_array = list(params_array) estimator = BackendEstimator(backend=backend) diff --git a/test/python/primitives/test_backend_sampler.py b/test/python/primitives/test_backend_sampler.py index e4ff7478d83f..b455e6b5dc17 100644 --- a/test/python/primitives/test_backend_sampler.py +++ b/test/python/primitives/test_backend_sampler.py @@ -259,7 +259,8 @@ def test_run_numpy_params(self, backend): qc = RealAmplitudes(num_qubits=2, reps=2) qc.measure_all() k = 5 - params_array = np.random.rand(k, qc.num_parameters) + rng = np.random.default_rng(12) + params_array = rng.random((k, qc.num_parameters)) params_list = params_array.tolist() params_list_array = list(params_array) sampler = BackendSampler(backend=backend) diff --git a/test/python/primitives/test_estimator.py b/test/python/primitives/test_estimator.py index e9a8516c2ab3..b86435828217 100644 --- a/test/python/primitives/test_estimator.py +++ b/test/python/primitives/test_estimator.py @@ -237,7 +237,8 @@ def test_run_numpy_params(self): qc = RealAmplitudes(num_qubits=2, reps=2) op = SparsePauliOp.from_list([("IZ", 1), ("XI", 2), ("ZY", -1)]) k = 5 - params_array = np.random.rand(k, qc.num_parameters) + rng = np.random.default_rng(12) + params_array = rng.random((k, qc.num_parameters)) params_list = params_array.tolist() params_list_array = list(params_array) estimator = Estimator() diff --git a/test/python/primitives/test_sampler.py b/test/python/primitives/test_sampler.py index 1ebfed5e2685..eac0886b30b1 100644 --- a/test/python/primitives/test_sampler.py +++ b/test/python/primitives/test_sampler.py @@ -324,7 +324,8 @@ def test_run_numpy_params(self): qc = RealAmplitudes(num_qubits=2, reps=2) qc.measure_all() k = 5 - params_array = np.random.rand(k, qc.num_parameters) + rng = np.random.default_rng(12) + params_array = rng.random((k, qc.num_parameters)) params_list = params_array.tolist() params_list_array = list(params_array) sampler = Sampler() diff --git a/test/python/primitives/test_statevector_estimator.py b/test/python/primitives/test_statevector_estimator.py index 1c2621acdbfa..c89af9cc33c6 100644 --- a/test/python/primitives/test_statevector_estimator.py +++ b/test/python/primitives/test_statevector_estimator.py @@ -236,13 +236,17 @@ def test_run_errors(self): est.run([(qc, op)], precision=-1).result() with self.assertRaises(ValueError): est.run([(qc, 1j * op)], precision=0.1).result() + with self.subTest("missing []"): + with self.assertRaisesRegex(ValueError, "An invalid Estimator pub-like was given"): + _ = est.run((qc, op)).result() def test_run_numpy_params(self): """Test for numpy array as parameter values""" qc = RealAmplitudes(num_qubits=2, reps=2) op = SparsePauliOp.from_list([("IZ", 1), ("XI", 2), ("ZY", -1)]) k = 5 - params_array = np.random.rand(k, qc.num_parameters) + rng = np.random.default_rng(12) + params_array = rng.random((k, qc.num_parameters)) params_list = params_array.tolist() params_list_array = list(params_array) estimator = StatevectorEstimator() diff --git a/test/python/primitives/test_statevector_sampler.py b/test/python/primitives/test_statevector_sampler.py index 0ae87bd7876c..cd0622b18de7 100644 --- a/test/python/primitives/test_statevector_sampler.py +++ b/test/python/primitives/test_statevector_sampler.py @@ -320,6 +320,12 @@ def test_run_errors(self): with self.subTest("zero shots, pub"): with self.assertRaises(ValueError): _ = sampler.run([SamplerPub(qc1, shots=0)]).result() + with self.subTest("missing []"): + with self.assertRaisesRegex(ValueError, "An invalid Sampler pub-like was given"): + _ = sampler.run(qc1).result() + with self.subTest("missing [] for pqc"): + with self.assertRaisesRegex(ValueError, "Note that if you want to run a single pub,"): + _ = sampler.run((qc2, [0, 1])).result() def test_run_empty_parameter(self): """Test for empty parameter"""