diff --git a/qiskit_aer/backends/aerbackend.py b/qiskit_aer/backends/aerbackend.py index e6d1db8f25..5bf9d516e7 100644 --- a/qiskit_aer/backends/aerbackend.py +++ b/qiskit_aer/backends/aerbackend.py @@ -24,7 +24,7 @@ from qiskit.circuit import QuantumCircuit, ParameterExpression, Delay from qiskit.compiler import assemble from qiskit.providers import BackendV2 as Backend -from qiskit.providers.models import BackendStatus +from qiskit.providers.models.backendstatus import BackendStatus from qiskit.pulse import Schedule, ScheduleBlock from qiskit.qobj import QasmQobj, PulseQobj from qiskit.result import Result diff --git a/qiskit_aer/noise/noise_model.py b/qiskit_aer/noise/noise_model.py index d040cb18cf..1a249c6562 100644 --- a/qiskit_aer/noise/noise_model.py +++ b/qiskit_aer/noise/noise_model.py @@ -23,7 +23,7 @@ from qiskit.circuit import Instruction, Delay from qiskit.providers import QubitProperties from qiskit.providers.exceptions import BackendPropertyError -from qiskit.providers.models import BackendProperties +from qiskit.providers.models.backendproperties import BackendProperties from qiskit.transpiler import PassManager from qiskit.utils import apply_prefix from .device.models import _excited_population, _truncate_t2_value diff --git a/qiskit_aer/primitives/estimator_v2.py b/qiskit_aer/primitives/estimator_v2.py index c7c45339d6..28815dd86e 100644 --- a/qiskit_aer/primitives/estimator_v2.py +++ b/qiskit_aer/primitives/estimator_v2.py @@ -107,7 +107,7 @@ def _validate_pubs(self, pubs: list[EstimatorPub]): ) def _run(self, pubs: list[EstimatorPub]) -> PrimitiveResult[PubResult]: - return PrimitiveResult([self._run_pub(pub) for pub in pubs]) + return PrimitiveResult([self._run_pub(pub) for pub in pubs], metadata={"version": 2}) def _run_pub(self, pub: EstimatorPub) -> PubResult: circuit = pub.circuit.copy() @@ -151,5 +151,9 @@ def _run_pub(self, pub: EstimatorPub) -> PubResult: evs = rng.normal(evs, precision, evs.shape) return PubResult( DataBin(evs=evs, stds=stds, shape=evs.shape), - metadata={"target_precision": precision, "simulator_metadata": result.metadata}, + metadata={ + "target_precision": precision, + "circuit_metadata": pub.circuit.metadata, + "simulator_metadata": result.metadata, + }, ) diff --git a/qiskit_aer/primitives/sampler_v2.py b/qiskit_aer/primitives/sampler_v2.py index 80d364001e..5cce4a3bef 100644 --- a/qiskit_aer/primitives/sampler_v2.py +++ b/qiskit_aer/primitives/sampler_v2.py @@ -151,7 +151,7 @@ def _run(self, pubs: list[SamplerPub]) -> PrimitiveResult[SamplerPubResult]: # reconstruct the result of pubs for i, pub_result in zip(lst, pub_results): results[i] = pub_result - return PrimitiveResult(results) + return PrimitiveResult(results, metadata={"version": 2}) def _run_pubs(self, pubs: list[SamplerPub], shots: int) -> list[SamplerPubResult]: """Compute results for pubs that all require the same value of ``shots``.""" @@ -192,6 +192,7 @@ def _run_pubs(self, pubs: list[SamplerPub], shots: int) -> list[SamplerPubResult p_v.shape, meas_info, max_num_bytes, + pub.circuit.metadata, result.metadata, ) ) @@ -206,7 +207,8 @@ def _postprocess_pub( shape: tuple[int, ...], meas_info: list[_MeasureInfo], max_num_bytes: int, - metadata: dict, + circuit_metadata: dict, + simulator_metadata: dict, ) -> SamplerPubResult: """Converts the memory data into an array of bit arrays with the shape of the pub.""" arrays = { @@ -224,7 +226,12 @@ def _postprocess_pub( item.creg_name: BitArray(arrays[item.creg_name], item.num_bits) for item in meas_info } return SamplerPubResult( - DataBin(**meas, shape=shape), metadata={"simulator_metadata": metadata} + DataBin(**meas, shape=shape), + metadata={ + "shots": shots, + "circuit_metadata": circuit_metadata, + "simulator_metadata": simulator_metadata, + }, ) diff --git a/releasenotes/notes/update-primitive-v2-metadata-bd7f5609e55ef2e7.yaml b/releasenotes/notes/update-primitive-v2-metadata-bd7f5609e55ef2e7.yaml new file mode 100644 index 0000000000..486a0f491d --- /dev/null +++ b/releasenotes/notes/update-primitive-v2-metadata-bd7f5609e55ef2e7.yaml @@ -0,0 +1,12 @@ +--- +features: + - | + The metadata of Primitives V2 implementations, + i.e., :class:`.SamplerV2` and :class:`.EstimatorV2`, + has been updated to match that of IBM quantum devices. + + * ``version`` and ``circuit_metadata`` are added for :class:`.SamplerV2` and :class:`.EstimatorV2` + * ``shots`` is added for :class:`.SamplerV2` + + Note that metadata of :class:`.EstimatorV2` does not have ``shots`` because + the class computes expectation values with ``save_expectation_value`` instruction and shots are not used. diff --git a/test/terra/primitives/test_estimator_v2.py b/test/terra/primitives/test_estimator_v2.py index 0499cceb43..cee5d28243 100644 --- a/test/terra/primitives/test_estimator_v2.py +++ b/test/terra/primitives/test_estimator_v2.py @@ -376,6 +376,37 @@ def test_iter_pub(self): np.testing.assert_allclose(result[0].data.evs, [-1.284366511861733], rtol=self._rtol) np.testing.assert_allclose(result[1].data.evs, [-1.284366511861733], rtol=self._rtol) + def test_metadata(self): + """Test for metadata""" + qc = QuantumCircuit(2) + qc2 = QuantumCircuit(2) + qc2.metadata = {"a": 1} + estimator = EstimatorV2(options=self._options) + pm = generate_preset_pass_manager(optimization_level=0, backend=self.backend) + qc, qc2 = pm.run([qc, qc2]) + op = SparsePauliOp("ZZ").apply_layout(qc.layout) + op2 = SparsePauliOp("ZZ").apply_layout(qc2.layout) + result = estimator.run([(qc, op), (qc2, op2)], precision=0.1).result() + + self.assertEqual(len(result), 2) + self.assertEqual(result.metadata, {"version": 2}) + + metadata = result[0].metadata + self.assertIsInstance(metadata["simulator_metadata"], dict) + del metadata["simulator_metadata"] + self.assertEqual( + metadata, + {"target_precision": 0.1, "circuit_metadata": qc.metadata}, + ) + + metadata = result[1].metadata + self.assertIsInstance(metadata["simulator_metadata"], dict) + del metadata["simulator_metadata"] + self.assertEqual( + result[1].metadata, + {"target_precision": 0.1, "circuit_metadata": qc2.metadata}, + ) + if __name__ == "__main__": unittest.main() diff --git a/test/terra/primitives/test_sampler_v2.py b/test/terra/primitives/test_sampler_v2.py index 7889135592..7522e9d943 100644 --- a/test/terra/primitives/test_sampler_v2.py +++ b/test/terra/primitives/test_sampler_v2.py @@ -674,6 +674,27 @@ def test_iter_pub(self): self._assert_allclose(result[0].data.meas, np.array({0: self._shots})) self._assert_allclose(result[1].data.meas, np.array({1: self._shots})) + def test_metadata(self): + """Test for metadata""" + qc = QuantumCircuit(2) + qc.measure_all() + qc2 = qc.copy() + qc2.metadata = {"a": 1} + sampler = SamplerV2(**self._options) + result = sampler.run([(qc, None, 10), (qc2, None, 20)]).result() + + self.assertEqual(len(result), 2) + self.assertEqual(result.metadata, {"version": 2}) + metadata = result[0].metadata + self.assertIsInstance(metadata["simulator_metadata"], dict) + del metadata["simulator_metadata"] + self.assertEqual(metadata, {"shots": 10, "circuit_metadata": qc.metadata}) + + metadata = result[1].metadata + self.assertIsInstance(metadata["simulator_metadata"], dict) + del metadata["simulator_metadata"] + self.assertEqual(metadata, {"shots": 20, "circuit_metadata": qc2.metadata}) + def test_seed(self): """Test for seed options""" with self.subTest("empty"):