diff --git a/qiskit/dagcircuit/dagnode.py b/qiskit/dagcircuit/dagnode.py index 439a43ef666e..f470527627e6 100644 --- a/qiskit/dagcircuit/dagnode.py +++ b/qiskit/dagcircuit/dagnode.py @@ -16,6 +16,17 @@ import warnings +from qiskit.circuit import Clbit + + +def _condition_as_indices(operation, bit_indices): + cond = getattr(operation, "condition", None) + if cond is None: + return None + bits, value = cond + indices = [bit_indices[bits]] if isinstance(bits, Clbit) else [bit_indices[x] for x in bits] + return indices, value + class DAGNode: """Parent class for DAGOpNode, DAGInNode, and DAGOutNode.""" @@ -77,16 +88,19 @@ def semantic_eq(node1, node2, bit_indices1=None, bit_indices2=None): if node1.op.name == node2.op.name and node1.name in {"barrier", "swap"}: return set(node1_qargs) == set(node2_qargs) - if node1_qargs == node2_qargs: - if node1_cargs == node2_cargs: - if getattr(node1.op, "condition", None) == getattr(node2.op, "condition", None): - if node1.op == node2.op: - return True - elif (isinstance(node1, DAGInNode) and isinstance(node2, DAGInNode)) or ( + return ( + node1_qargs == node2_qargs + and node1_cargs == node2_cargs + and ( + _condition_as_indices(node1.op, bit_indices1) + == _condition_as_indices(node2.op, bit_indices2) + ) + and node1.op == node2.op + ) + if (isinstance(node1, DAGInNode) and isinstance(node2, DAGInNode)) or ( isinstance(node1, DAGOutNode) and isinstance(node2, DAGOutNode) ): - if bit_indices1.get(node1.wire, None) == bit_indices2.get(node2.wire, None): - return True + return bit_indices1.get(node1.wire, None) == bit_indices2.get(node2.wire, None) return False diff --git a/releasenotes/notes/fix-circuit-condition-compare-d8d85e5ca47c1416.yaml b/releasenotes/notes/fix-circuit-condition-compare-d8d85e5ca47c1416.yaml new file mode 100644 index 000000000000..3c611e29bfa6 --- /dev/null +++ b/releasenotes/notes/fix-circuit-condition-compare-d8d85e5ca47c1416.yaml @@ -0,0 +1,8 @@ +--- +fixes: + - | + The equality checkers for :class:`.QuantumCircuit` and :class:`.DAGCircuit` + (with objects of the same type) will now correctly handle conditions on single + bits. Previously, these would produce false negatives for equality, as the + bits would use "exact" equality checks instead of the "semantic" checks the rest + of the properties of circuit instructions get. diff --git a/test/python/circuit/test_circuit_operations.py b/test/python/circuit/test_circuit_operations.py index 426683e62a1a..a0e87a980606 100644 --- a/test/python/circuit/test_circuit_operations.py +++ b/test/python/circuit/test_circuit_operations.py @@ -1162,6 +1162,36 @@ def test_compare_two_different_circuits(self): self.assertFalse(qc1 == qc2) + def test_compare_circuits_with_single_bit_conditions(self): + """Test that circuits with single-bit conditions can be compared correctly.""" + qreg = QuantumRegister(1, name="q") + creg = ClassicalRegister(1, name="c") + qc1 = QuantumCircuit(qreg, creg, [Clbit()]) + qc1.x(0).c_if(qc1.cregs[0], 1) + qc1.x(0).c_if(qc1.clbits[-1], True) + qc2 = QuantumCircuit(qreg, creg, [Clbit()]) + qc2.x(0).c_if(qc2.cregs[0], 1) + qc2.x(0).c_if(qc2.clbits[-1], True) + self.assertEqual(qc1, qc2) + + # Order of operations transposed. + qc1 = QuantumCircuit(qreg, creg, [Clbit()]) + qc1.x(0).c_if(qc1.cregs[0], 1) + qc1.x(0).c_if(qc1.clbits[-1], True) + qc2 = QuantumCircuit(qreg, creg, [Clbit()]) + qc2.x(0).c_if(qc2.clbits[-1], True) + qc2.x(0).c_if(qc2.cregs[0], 1) + self.assertNotEqual(qc1, qc2) + + # Single-bit condition values not the same. + qc1 = QuantumCircuit(qreg, creg, [Clbit()]) + qc1.x(0).c_if(qc1.cregs[0], 1) + qc1.x(0).c_if(qc1.clbits[-1], True) + qc2 = QuantumCircuit(qreg, creg, [Clbit()]) + qc2.x(0).c_if(qc2.cregs[0], 1) + qc2.x(0).c_if(qc2.clbits[-1], False) + self.assertNotEqual(qc1, qc2) + def test_compare_a_circuit_with_none(self): """Test to compare that a circuit is different to None.""" qc1 = QuantumCircuit(2, 2) diff --git a/test/python/dagcircuit/test_dagcircuit.py b/test/python/dagcircuit/test_dagcircuit.py index 8024e13ad44d..ad7f2167a197 100644 --- a/test/python/dagcircuit/test_dagcircuit.py +++ b/test/python/dagcircuit/test_dagcircuit.py @@ -1462,6 +1462,36 @@ def test_node_params_equal_unequal(self): self.assertEqual(dag1, dag2) self.assertNotEqual(dag2, dag3) + def test_semantic_conditions(self): + """Test that the semantic equality is applied to the bits in conditions as well.""" + qreg = QuantumRegister(1, name="q") + creg = ClassicalRegister(1, name="c") + qc1 = QuantumCircuit(qreg, creg, [Clbit()]) + qc1.x(0).c_if(qc1.cregs[0], 1) + qc1.x(0).c_if(qc1.clbits[-1], True) + qc2 = QuantumCircuit(qreg, creg, [Clbit()]) + qc2.x(0).c_if(qc2.cregs[0], 1) + qc2.x(0).c_if(qc2.clbits[-1], True) + self.assertEqual(circuit_to_dag(qc1), circuit_to_dag(qc2)) + + # Order of operations transposed. + qc1 = QuantumCircuit(qreg, creg, [Clbit()]) + qc1.x(0).c_if(qc1.cregs[0], 1) + qc1.x(0).c_if(qc1.clbits[-1], True) + qc2 = QuantumCircuit(qreg, creg, [Clbit()]) + qc2.x(0).c_if(qc2.clbits[-1], True) + qc2.x(0).c_if(qc2.cregs[0], 1) + self.assertNotEqual(circuit_to_dag(qc1), circuit_to_dag(qc2)) + + # Single-bit condition values not the same. + qc1 = QuantumCircuit(qreg, creg, [Clbit()]) + qc1.x(0).c_if(qc1.cregs[0], 1) + qc1.x(0).c_if(qc1.clbits[-1], True) + qc2 = QuantumCircuit(qreg, creg, [Clbit()]) + qc2.x(0).c_if(qc2.cregs[0], 1) + qc2.x(0).c_if(qc2.clbits[-1], False) + self.assertNotEqual(circuit_to_dag(qc1), circuit_to_dag(qc2)) + class TestDagSubstitute(QiskitTestCase): """Test substituting a dag node with a sub-dag"""