From e141008e64c283c27db02f9b9b22f26b103d2703 Mon Sep 17 00:00:00 2001 From: mhucka Date: Sun, 10 Aug 2025 02:11:46 +0000 Subject: [PATCH 1/4] Fix #855: don't call cirq.unitary() on non-unitary matrices In Cirq 1.6.0, the function [`unitary(...)`](https://github.com/quantumlib/Cirq/blob/fe0dc2187ca3269c178526e8ba41083fa1a467c9/cirq-core/cirq/protocols/unitary_protocol.py#L81) in `cirq-core/cirq/protocols/unitary_protocol.py` changed. Whereas previously, if the value passed in was a NumPy array, it the function returned the array directly without doing anything else, `unitary(...)` was changed in [PR the array really _is_ unitary and raise an exception if it isn't. This arguably corrected a fault in `unitary(...)` because with the previous definition, it would return non-unitary results if given a NumPy array that wasn't already unitary. However, in one of the tests in qsim, it had the result of revealing a small bug: the test case was knowingly invoking `unitary(...)` with a non-unitary matrix. This led to exceptions being raised in Cirq 1.6.0 but not in prior versions. The solution in this case turned out to be very simple: the call to `cirq.unitary(...)` on line 1060 was unnecessary because the previous version of `unitary(...)` simply returned it right away. Removing the call kept the previous behavior (which was to use a non-unitary matrix in that situation) and works in both Cirq 1.5.0 and 1.6.0. --- qsimcirq_tests/qsimcirq_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qsimcirq_tests/qsimcirq_test.py b/qsimcirq_tests/qsimcirq_test.py index 6c81298df..b36229d8f 100644 --- a/qsimcirq_tests/qsimcirq_test.py +++ b/qsimcirq_tests/qsimcirq_test.py @@ -1057,7 +1057,7 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def _mixture_(self): - return [(prob, cirq.unitary(op)) for prob, op, in self._prob_op_pairs] + return [(prob, op) for prob, op, in self._prob_op_pairs] @pytest.mark.parametrize( From a82b6ec1b9731b3358cfcf28739db0ae972bbb89 Mon Sep 17 00:00:00 2001 From: mhucka Date: Sun, 10 Aug 2025 02:16:33 +0000 Subject: [PATCH 2/4] Fix formatting complaint from black --- qsimcirq_tests/qsimcirq_test.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qsimcirq_tests/qsimcirq_test.py b/qsimcirq_tests/qsimcirq_test.py index b36229d8f..4de12b782 100644 --- a/qsimcirq_tests/qsimcirq_test.py +++ b/qsimcirq_tests/qsimcirq_test.py @@ -2025,11 +2025,11 @@ def test_qsimcirq_identity_expectation_value(): for w, pauli in objs: pauli = pauli[::-1] hamiltonian += float(w) * cirq.PauliString( - cirq.I(cirq.LineQubit(i)) - if p == "I" - else cirq.Z(cirq.LineQubit(i)) - if p == "Z" - else None + ( + cirq.I(cirq.LineQubit(i)) + if p == "I" + else cirq.Z(cirq.LineQubit(i)) if p == "Z" else None + ) for i, p in enumerate(pauli) ) From 3576529ed55bffc637f69f87146ed73ba0a6dd43 Mon Sep 17 00:00:00 2001 From: mhucka Date: Mon, 11 Aug 2025 15:36:07 +0000 Subject: [PATCH 3/4] Add comment explaining what's happening --- qsimcirq_tests/qsimcirq_test.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/qsimcirq_tests/qsimcirq_test.py b/qsimcirq_tests/qsimcirq_test.py index 4de12b782..94d675d9b 100644 --- a/qsimcirq_tests/qsimcirq_test.py +++ b/qsimcirq_tests/qsimcirq_test.py @@ -1057,6 +1057,16 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def _mixture_(self): + # Cirq's mixture() function in mixture_protocol.py returns tuples of + # the form (probability, unitary peration). It does this by applying + # Cirq's unitary() function to the second elements of the tuples + # returned from here. Now, the values in self._prob_op_pairs will be + # tuples of the form (probability, NoiseStep). NoiseStep defines a + # _unitary_() method that simply returns the array as-is. Thus, when + # Cirq's mixture() function gets the value returned here and calls + # unitary() on those NoiseStep objects, the values unitary() returns + # will not actually be unitary. This is done knowingly. The nonunitary + # values are eventually normalized in test_multi_qubit_noise(). return [(prob, op) for prob, op, in self._prob_op_pairs] From 4f454037350aa6b742c8ef3af14b6202b5dc252a Mon Sep 17 00:00:00 2001 From: Michael Hucka Date: Tue, 12 Aug 2025 13:52:31 -0700 Subject: [PATCH 4/4] Update qsimcirq_test.py Fix typo spotted by @pavoljuhas --- qsimcirq_tests/qsimcirq_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qsimcirq_tests/qsimcirq_test.py b/qsimcirq_tests/qsimcirq_test.py index 94d675d9b..66c41b559 100644 --- a/qsimcirq_tests/qsimcirq_test.py +++ b/qsimcirq_tests/qsimcirq_test.py @@ -1058,7 +1058,7 @@ def __init__(self, *args, **kwargs): def _mixture_(self): # Cirq's mixture() function in mixture_protocol.py returns tuples of - # the form (probability, unitary peration). It does this by applying + # the form (probability, unitary operation). It does this by applying # Cirq's unitary() function to the second elements of the tuples # returned from here. Now, the values in self._prob_op_pairs will be # tuples of the form (probability, NoiseStep). NoiseStep defines a