From b751a8c3d25aa57f853bb74ded4e3472b4081d9f Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Tue, 11 Feb 2025 19:06:11 +0000 Subject: [PATCH] Consider `unit` in `Delay` comparisons (#13816) The `unit` field was previously ignored, allowing delays in `dt` units to compare equal to those in `s`. This commit does not add additional on-the-fly unit conversion to the comparison: if the user specified durations in different time multiples, they may have had a reason to consider them non-equal. This could be revisiting after the new system for duration handling lands (i.e. the new functionality for `stretch` and other delayed scheduling). (cherry picked from commit 32eae98a2282fdd056a84b12836b124e1726b843) --- qiskit/circuit/delay.py | 5 +++ .../notes/delay-compare-b7ecb26b94ff0cd3.yaml | 5 +++ test/python/circuit/test_delay.py | 37 +++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 releasenotes/notes/delay-compare-b7ecb26b94ff0cd3.yaml diff --git a/qiskit/circuit/delay.py b/qiskit/circuit/delay.py index 2f0613acb932..96d7168a80dc 100644 --- a/qiskit/circuit/delay.py +++ b/qiskit/circuit/delay.py @@ -81,6 +81,11 @@ def to_matrix(self) -> np.ndarray: """ return self.__array__(dtype=complex) + def __eq__(self, other): + return ( + isinstance(other, Delay) and self.unit == other.unit and self._compare_parameters(other) + ) + def __repr__(self): """Return the official string representing the delay.""" return f"{self.__class__.__name__}(duration={self.params[0]}[unit={self.unit}])" diff --git a/releasenotes/notes/delay-compare-b7ecb26b94ff0cd3.yaml b/releasenotes/notes/delay-compare-b7ecb26b94ff0cd3.yaml new file mode 100644 index 000000000000..583dc1752838 --- /dev/null +++ b/releasenotes/notes/delay-compare-b7ecb26b94ff0cd3.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Comparisons of :class:`~.circuit.Delay` instructions, including within circuits, now require the + units to be equal as well as the duration value. diff --git a/test/python/circuit/test_delay.py b/test/python/circuit/test_delay.py index 4259c5604606..fb989789e97d 100644 --- a/test/python/circuit/test_delay.py +++ b/test/python/circuit/test_delay.py @@ -99,6 +99,43 @@ def test_to_matrix_return_identity_matrix(self): expected = np.array([[1, 0], [0, 1]], dtype=complex) self.assertTrue(np.array_equal(actual, expected)) + def test_equality(self): + # At the time `__eq__` was specialised for `Delay`, the class was undergoing changes and + # moving to Rust, so we didn't also modify the Python-space semantics to declare equality + # between (say) 1000ms and 1s. We could revisit that decision once the data model settles. + # + # This test then deliberately doesn't assert about mixed-scale comparisons, only comparisons + # between the same units, and 'dt' to absolute times. + def circuit_from(delay): + out = QuantumCircuit(1) + out.append(delay, [0], []) + return out + + a = Parameter("a") + left_instructions, right_instructions = [], [] + left_circuits, right_circuits = [], [] + for unit in ("s", "ms", "us", "ns", "ps", "dt"): + for base in (left_instructions, right_instructions): + base.append(Delay(1, unit)) + base.append(Delay(5.0, unit)) + base.append(Delay(a, unit)) + for base in (left_circuits, right_circuits): + base.append(circuit_from(Delay(1, unit))) + base.append(circuit_from(Delay(5.0, unit))) + base.append(circuit_from(Delay(a, unit))) + self.assertEqual(left_instructions, right_instructions) + self.assertEqual(left_circuits, right_circuits) + + # We can't do all the non-equal tests in a single list comparison, since any single list + # failure would mask any spurious successes. + for unit in ("s", "ms", "us", "ns", "ps"): + self.assertNotEqual(Delay(2, unit), Delay(2, "dt")) + self.assertNotEqual(circuit_from(Delay(2, unit)), circuit_from(Delay(2, "dt"))) + self.assertNotEqual(Delay(2, "dt"), Delay(2, unit)) + self.assertNotEqual(circuit_from(Delay(2, "dt")), circuit_from(Delay(2, unit))) + self.assertNotEqual(Delay(a, unit), Delay(a, "dt")) + self.assertNotEqual(circuit_from(Delay(a, unit)), circuit_from(Delay(a, "dt"))) + class TestParameterizedDelay(QiskitTestCase): """Test delay instruction with parameterized duration."""