From 36ea0612a6c837c509e0873e90689381b050c71e Mon Sep 17 00:00:00 2001 From: --get-all Date: Fri, 24 Mar 2023 09:35:00 -0400 Subject: [PATCH 1/9] Fix QuantumCircuit.metadata return annotation The QuantumCircuit.metadata return annotation was previously just ``dict``, whereas ``None`` is a valid value. This means that static type checkers would have no problems with statements like ``circuit.metadata.get("name", 0)``. --- qiskit/circuit/quantumcircuit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index 7c304f63af4a..61bf5f567474 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -449,7 +449,7 @@ def has_calibration_for(self, instr_context: Tuple): return (qubits, params) in self.calibrations[instr.name] @property - def metadata(self) -> dict: + def metadata(self) -> Union[dict, None]: """The user provided metadata associated with the circuit The metadata for the circuit is a user provided ``dict`` of metadata From 42cfe6bf1936aafd559bdaa1b99e96138670dffb Mon Sep 17 00:00:00 2001 From: --get-all Date: Fri, 24 Mar 2023 12:04:02 -0400 Subject: [PATCH 2/9] Change QuantumCircuit.metadata to always be dictionary Previous to this change, the QuantumCircuit.metadata could be set to, and return None instead of a dictionary. --- qiskit/circuit/quantumcircuit.py | 21 ++++++++++++------- .../python/circuit/test_circuit_properties.py | 12 +++++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index 61bf5f567474..1b5e12df52af 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -22,6 +22,7 @@ import multiprocessing as mp import string import re +import warnings from collections import OrderedDict, defaultdict, namedtuple from typing import ( Union, @@ -298,9 +299,7 @@ def __init__( self.duration = None self.unit = "dt" - if not isinstance(metadata, dict) and metadata is not None: - raise TypeError("Only a dictionary or None is accepted for circuit metadata") - self._metadata = metadata + self.metadata = {} if metadata is None else metadata @staticmethod def from_instructions( @@ -449,8 +448,8 @@ def has_calibration_for(self, instr_context: Tuple): return (qubits, params) in self.calibrations[instr.name] @property - def metadata(self) -> Union[dict, None]: - """The user provided metadata associated with the circuit + def metadata(self) -> dict: + """The user provided metadata associated with the circuit. The metadata for the circuit is a user provided ``dict`` of metadata for the circuit. It will not be used to influence the execution or @@ -464,9 +463,17 @@ def metadata(self) -> Union[dict, None]: @metadata.setter def metadata(self, metadata: Optional[dict]): """Update the circuit metadata""" - if not isinstance(metadata, dict) and metadata is not None: - raise TypeError("Only a dictionary or None is accepted for circuit metadata") + if metadata is None: + metadata = {} + warnings.warn( + "Setting metadata to None was deprecated in Terra 0.24.0 and this ability will be " + "removed in a future release. Instead, set metadata to an empty dictionary.", + DeprecationWarning, + ) + self._metadata = metadata + if not isinstance(self._metadata, dict): + raise TypeError("Only a dictionary is accepted for circuit metadata") def __str__(self) -> str: return str(self.draw(output="text")) diff --git a/test/python/circuit/test_circuit_properties.py b/test/python/circuit/test_circuit_properties.py index 12c3ecd9b0a2..494e60fcde89 100644 --- a/test/python/circuit/test_circuit_properties.py +++ b/test/python/circuit/test_circuit_properties.py @@ -1267,6 +1267,18 @@ def test_metadata_copy_does_not_share_state(self): self.assertEqual(qc1.metadata["a"], 0) + def test_metadata_is_dict(self): + """Verify setting metadata to None in the constructor results in an empty dict.""" + qc = QuantumCircuit(1) + metadata1 = qc.metadata + self.assertEqual(metadata1, {}) + + def test_metadata_raises(self): + """Test that we must set metadata to a dict.""" + qc = QuantumCircuit(1) + with self.assertRaises(TypeError): + qc.metadata = 1 + def test_scheduling(self): """Test cannot return schedule information without scheduling.""" qc = QuantumCircuit(2) From d097c710a92ba3e5cdbf367ae4b19c14708d0ecd Mon Sep 17 00:00:00 2001 From: --get-all Date: Fri, 24 Mar 2023 12:57:16 -0400 Subject: [PATCH 3/9] Fix cases where QuantumCircuit.metadata is set to None by a DAG --- qiskit/converters/dag_to_circuit.py | 2 +- qiskit/converters/dagdependency_to_circuit.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit/converters/dag_to_circuit.py b/qiskit/converters/dag_to_circuit.py index 5a32f0bba1e1..8a37603ab4de 100644 --- a/qiskit/converters/dag_to_circuit.py +++ b/qiskit/converters/dag_to_circuit.py @@ -63,7 +63,7 @@ def dag_to_circuit(dag, copy_operations=True): name=name, global_phase=dag.global_phase, ) - circuit.metadata = dag.metadata + circuit.metadata = dag.metadata or {} circuit.calibrations = dag.calibrations for node in dag.topological_op_nodes(): diff --git a/qiskit/converters/dagdependency_to_circuit.py b/qiskit/converters/dagdependency_to_circuit.py index 2c9a0d4389cb..36e920016422 100644 --- a/qiskit/converters/dagdependency_to_circuit.py +++ b/qiskit/converters/dagdependency_to_circuit.py @@ -32,7 +32,7 @@ def dagdependency_to_circuit(dagdependency): *dagdependency.cregs.values(), name=name, ) - circuit.metadata = dagdependency.metadata + circuit.metadata = dagdependency.metadata or {} circuit.calibrations = dagdependency.calibrations From 1ebc5260e2f0745f5f2ab3b5f0bb91302c867682 Mon Sep 17 00:00:00 2001 From: --get-all Date: Fri, 24 Mar 2023 13:06:58 -0400 Subject: [PATCH 4/9] Added release notes --- ...circuit_metadata_always_dict-49015896dfa49d33.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 releasenotes/notes/circuit_metadata_always_dict-49015896dfa49d33.yaml diff --git a/releasenotes/notes/circuit_metadata_always_dict-49015896dfa49d33.yaml b/releasenotes/notes/circuit_metadata_always_dict-49015896dfa49d33.yaml new file mode 100644 index 000000000000..9a908f24ade3 --- /dev/null +++ b/releasenotes/notes/circuit_metadata_always_dict-49015896dfa49d33.yaml @@ -0,0 +1,11 @@ +upgrade: + - | + The :class:`.QuantumCircuit` :attr:`.QuantumCircuit.metadata` attribute now + always returns a dictionary, and can only be set to a dictionary. Previously, + its default value was ``None``, and could be manually set to ``None`` or a + dictionary. +deprecations: + - | + Setting the :class:`.QuantumCircuit` :attr:`.QuantumCircuit.metadata` attribute + to ``None`` has been deprecated. Instead, users should set it to an empty + dictionary if they want it to contain no data. From 092fb4b3c3a70543a54426103e37d023dee49500 Mon Sep 17 00:00:00 2001 From: --get-all Date: Fri, 24 Mar 2023 13:29:22 -0400 Subject: [PATCH 5/9] Minor updates --- qiskit/circuit/quantumcircuit.py | 5 ++--- test/python/circuit/test_circuit_properties.py | 7 +++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index 60fd8049e32e..73aceefcf8a3 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -470,10 +470,9 @@ def metadata(self, metadata: Optional[dict]): "removed in a future release. Instead, set metadata to an empty dictionary.", DeprecationWarning, ) - - self._metadata = metadata - if not isinstance(self._metadata, dict): + elif not isinstance(metadata, dict): raise TypeError("Only a dictionary is accepted for circuit metadata") + self._metadata = metadata def __str__(self) -> str: return str(self.draw(output="text")) diff --git a/test/python/circuit/test_circuit_properties.py b/test/python/circuit/test_circuit_properties.py index 494e60fcde89..85b0ff64c180 100644 --- a/test/python/circuit/test_circuit_properties.py +++ b/test/python/circuit/test_circuit_properties.py @@ -1279,6 +1279,13 @@ def test_metadata_raises(self): with self.assertRaises(TypeError): qc.metadata = 1 + def test_metdata_deprectation(self): + """Test that setting metadata to None emits a deprecation.""" + qc = QuantumCircuit(1) + with self.assertWarns(DeprecationWarning): + qc.metadata = None + self.assertEqual(qc.metadata, {}) + def test_scheduling(self): """Test cannot return schedule information without scheduling.""" qc = QuantumCircuit(2) From cc3e67eb7978ee0adef3d7841fbad09c0746aafd Mon Sep 17 00:00:00 2001 From: --get-all Date: Fri, 24 Mar 2023 13:32:33 -0400 Subject: [PATCH 6/9] Change default metadata value of DAGCircuit/Dependency to {} Previously it was None --- qiskit/converters/dag_to_circuit.py | 2 +- qiskit/converters/dagdependency_to_circuit.py | 2 +- qiskit/dagcircuit/dagcircuit.py | 2 +- qiskit/dagcircuit/dagdependency.py | 2 +- .../circuit_metadata_always_dict-49015896dfa49d33.yaml | 4 ++++ test/python/dagcircuit/test_dagcircuit.py | 6 ++++++ test/python/dagcircuit/test_dagdependency.py | 6 ++++++ 7 files changed, 20 insertions(+), 4 deletions(-) diff --git a/qiskit/converters/dag_to_circuit.py b/qiskit/converters/dag_to_circuit.py index 8a37603ab4de..5a32f0bba1e1 100644 --- a/qiskit/converters/dag_to_circuit.py +++ b/qiskit/converters/dag_to_circuit.py @@ -63,7 +63,7 @@ def dag_to_circuit(dag, copy_operations=True): name=name, global_phase=dag.global_phase, ) - circuit.metadata = dag.metadata or {} + circuit.metadata = dag.metadata circuit.calibrations = dag.calibrations for node in dag.topological_op_nodes(): diff --git a/qiskit/converters/dagdependency_to_circuit.py b/qiskit/converters/dagdependency_to_circuit.py index 36e920016422..2c9a0d4389cb 100644 --- a/qiskit/converters/dagdependency_to_circuit.py +++ b/qiskit/converters/dagdependency_to_circuit.py @@ -32,7 +32,7 @@ def dagdependency_to_circuit(dagdependency): *dagdependency.cregs.values(), name=name, ) - circuit.metadata = dagdependency.metadata or {} + circuit.metadata = dagdependency.metadata circuit.calibrations = dagdependency.calibrations diff --git a/qiskit/dagcircuit/dagcircuit.py b/qiskit/dagcircuit/dagcircuit.py index ed3dfc1fd618..9b25dc655e4a 100644 --- a/qiskit/dagcircuit/dagcircuit.py +++ b/qiskit/dagcircuit/dagcircuit.py @@ -60,7 +60,7 @@ def __init__(self): self.name = None # Circuit metadata - self.metadata = None + self.metadata = {} # Set of wires (Register,idx) in the dag self._wires = set() diff --git a/qiskit/dagcircuit/dagdependency.py b/qiskit/dagcircuit/dagdependency.py index b7071748d2fa..05572c9e0802 100644 --- a/qiskit/dagcircuit/dagdependency.py +++ b/qiskit/dagcircuit/dagdependency.py @@ -91,7 +91,7 @@ def __init__(self): self.name = None # Circuit metadata - self.metadata = None + self.metadata = {} # Directed multigraph whose nodes are operations(gates) and edges # represent non-commutativity between two gates. diff --git a/releasenotes/notes/circuit_metadata_always_dict-49015896dfa49d33.yaml b/releasenotes/notes/circuit_metadata_always_dict-49015896dfa49d33.yaml index 9a908f24ade3..772daee423c1 100644 --- a/releasenotes/notes/circuit_metadata_always_dict-49015896dfa49d33.yaml +++ b/releasenotes/notes/circuit_metadata_always_dict-49015896dfa49d33.yaml @@ -4,6 +4,10 @@ upgrade: always returns a dictionary, and can only be set to a dictionary. Previously, its default value was ``None``, and could be manually set to ``None`` or a dictionary. + - | + The default value of ``metadata`` in both :class:`.DAGCircuit` and + :class:`.DAGDependency` has been changed from ``None`` to ``{}`` for compatibility + with a similar attribute of :class:`.QuantumCircuit`. deprecations: - | Setting the :class:`.QuantumCircuit` :attr:`.QuantumCircuit.metadata` attribute diff --git a/test/python/dagcircuit/test_dagcircuit.py b/test/python/dagcircuit/test_dagcircuit.py index ae6e6afe53d4..7e4fe991792a 100644 --- a/test/python/dagcircuit/test_dagcircuit.py +++ b/test/python/dagcircuit/test_dagcircuit.py @@ -1225,6 +1225,12 @@ def test_circuit_factors(self): """Test number of separable factors in circuit.""" self.assertEqual(self.dag.num_tensor_factors(), 2) + def test_default_metadata_value(self): + """Test that the default DAGCircuit metadata is valid QuantumCircuit data.""" + qc = QuantumCircuit(1) + qc.metadata = self.dag.metadata + self.assertEqual(qc.metadata, {}) + class TestCircuitControlFlowProperties(QiskitTestCase): """Properties tests of DAGCircuit with control-flow instructions.""" diff --git a/test/python/dagcircuit/test_dagdependency.py b/test/python/dagcircuit/test_dagdependency.py index 011584dded2b..348a673f4004 100644 --- a/test/python/dagcircuit/test_dagdependency.py +++ b/test/python/dagcircuit/test_dagdependency.py @@ -319,6 +319,12 @@ def test_dag_depth_empty(self): dag = circuit_to_dagdependency(qc) self.assertEqual(dag.depth(), 0) + def test_default_metadata_value(self): + """Test that the default DAGDependency metadata is valid QuantumCircuit data.""" + qc = QuantumCircuit(1) + qc.metadata = self.dag.metadata + self.assertEqual(qc.metadata, {}) + if __name__ == "__main__": unittest.main() From 60ca7b84c6c119d3fd88a1d93106f5bef9272c5a Mon Sep 17 00:00:00 2001 From: --get-all Date: Fri, 24 Mar 2023 13:35:40 -0400 Subject: [PATCH 7/9] fix typos --- test/python/circuit/test_circuit_properties.py | 2 +- test/python/dagcircuit/test_dagcircuit.py | 2 +- test/python/dagcircuit/test_dagdependency.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/python/circuit/test_circuit_properties.py b/test/python/circuit/test_circuit_properties.py index 85b0ff64c180..67c6cca5518b 100644 --- a/test/python/circuit/test_circuit_properties.py +++ b/test/python/circuit/test_circuit_properties.py @@ -1280,7 +1280,7 @@ def test_metadata_raises(self): qc.metadata = 1 def test_metdata_deprectation(self): - """Test that setting metadata to None emits a deprecation.""" + """Test that setting metadata to None emits a deprecation warning.""" qc = QuantumCircuit(1) with self.assertWarns(DeprecationWarning): qc.metadata = None diff --git a/test/python/dagcircuit/test_dagcircuit.py b/test/python/dagcircuit/test_dagcircuit.py index 7e4fe991792a..c934af733f00 100644 --- a/test/python/dagcircuit/test_dagcircuit.py +++ b/test/python/dagcircuit/test_dagcircuit.py @@ -1226,7 +1226,7 @@ def test_circuit_factors(self): self.assertEqual(self.dag.num_tensor_factors(), 2) def test_default_metadata_value(self): - """Test that the default DAGCircuit metadata is valid QuantumCircuit data.""" + """Test that the default DAGCircuit metadata is valid QuantumCircuit metadata.""" qc = QuantumCircuit(1) qc.metadata = self.dag.metadata self.assertEqual(qc.metadata, {}) diff --git a/test/python/dagcircuit/test_dagdependency.py b/test/python/dagcircuit/test_dagdependency.py index 348a673f4004..ca6890bb40a3 100644 --- a/test/python/dagcircuit/test_dagdependency.py +++ b/test/python/dagcircuit/test_dagdependency.py @@ -320,7 +320,7 @@ def test_dag_depth_empty(self): self.assertEqual(dag.depth(), 0) def test_default_metadata_value(self): - """Test that the default DAGDependency metadata is valid QuantumCircuit data.""" + """Test that the default DAGDependency metadata is valid QuantumCircuit metadata.""" qc = QuantumCircuit(1) qc.metadata = self.dag.metadata self.assertEqual(qc.metadata, {}) From 64816cc5e4b299d0f2e0d5b8321337ed62848ed4 Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Thu, 13 Apr 2023 21:01:13 +0100 Subject: [PATCH 8/9] Fix stacklevel of emitted warning --- qiskit/circuit/quantumcircuit.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index 5b9e29214b5e..5c9f1e263657 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -488,6 +488,7 @@ def metadata(self, metadata: dict | None): "Setting metadata to None was deprecated in Terra 0.24.0 and this ability will be " "removed in a future release. Instead, set metadata to an empty dictionary.", DeprecationWarning, + stacklevel=2, ) elif not isinstance(metadata, dict): raise TypeError("Only a dictionary is accepted for circuit metadata") From a4f0db70bcbd464f45d2e872ee366ec1127e000c Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Thu, 13 Apr 2023 21:01:23 +0100 Subject: [PATCH 9/9] Add warning suppression for Aer behaviour This will be fixed in Aer in the near future in a backwards-compatible manner, so in order to put the fix out into Terra while Aer's deployment issues are worked out (run out of space on PyPI!), we can add a very targetted warning suppression to the tests. --- qiskit/test/base.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/qiskit/test/base.py b/qiskit/test/base.py index e81226cc1b87..ac9da7a02a2d 100644 --- a/qiskit/test/base.py +++ b/qiskit/test/base.py @@ -240,6 +240,13 @@ def setUpClass(cls): ] for msg in allow_DeprecationWarning_message: warnings.filterwarnings("default", category=DeprecationWarning, message=msg) + # This warning should be fixed once Qiskit/qiskit-aer#1761 is in a release version of Aer. + warnings.filterwarnings( + "default", + category=DeprecationWarning, + module="qiskit_aer.*", + message="Setting metadata to None.*", + ) class FullQiskitTestCase(QiskitTestCase):