-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Fixed commutation checking between two Pauli product measurements #16023
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
19ab6ba
2b4b48e
6323efa
8ef7b5b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| --- | ||
| fixes: | ||
| - | | ||
| Fixed commutativity check between two :class:`.PauliProductMeasurement` instructions | ||
| in the case that both instructions measure to the same classical bit. In this case, | ||
| the later measurement overwrites the result of the earlier measurement, and consequently, | ||
| interchanging the two measurement instructions inside the quantum circuit is generally | ||
| invalid. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -525,10 +525,11 @@ def test_pauli_based_gates(self, gate_type): | |
| for p1, q1, p2, q2, expected in cases: | ||
| if p1 == "I" and gate_type == "measure": | ||
| continue # PPM doesn't support all-identity gates | ||
| c1, c2 = ([0], [1]) if gate_type == "measure" else ([], []) | ||
|
|
||
| gate1 = build_pauli_gate(p1, gate_type) | ||
| gate2 = build_pauli_gate(p2, gate_type) | ||
| self.assertEqual(expected, scc.commute(gate1, q1, [], gate2, q2, [])) | ||
| self.assertEqual(expected, scc.commute(gate1, q1, c1, gate2, q2, c2)) | ||
|
|
||
| @data( | ||
| ("pauli", "measure"), | ||
|
|
@@ -544,10 +545,69 @@ def test_mix_pauli_gates(self, gate_type1, gate_type2): | |
| ] | ||
|
|
||
| for p1, q1, p2, q2, expected in cases: | ||
| c1 = [0] if gate_type1 == "measure" else [] | ||
| c2 = [1] if gate_type2 == "measure" else [] | ||
|
|
||
| gate1 = build_pauli_gate(p1, gate_type1) | ||
| gate2 = build_pauli_gate(p2, gate_type2) | ||
|
|
||
| with self.subTest(p1=p1, p2=p2): | ||
| self.assertEqual(expected, scc.commute(gate1, q1, [], gate2, q2, [])) | ||
| self.assertEqual(expected, scc.commute(gate1, q1, c1, gate2, q2, c2)) | ||
|
|
||
| def test_ppms_with_same_clbit(self): | ||
| """Test commutativity of two Pauli product measurements with the same clbit.""" | ||
|
|
||
| # Each case represents (pauli1, qubits1, pauli2, qubits2, expected result). | ||
| # Recall that the convention is that pauli strings are read right-to-left, i.e. | ||
| # pauli strings and qubits are in reverse order relative to each other. | ||
| cases = [ | ||
| # different Paulis | ||
| ("XXII", [1, 0, 2, 3], "IIZZ", [1, 0, 2, 3], False), | ||
| # different qubits | ||
| ("XYIZ", [1, 0, 2, 3], "XYIZ", [1, 0, 4, 3], False), | ||
| # different Paulis (including sign) | ||
| ( | ||
| "ZXII", | ||
| [1, 0, 2, 3], | ||
| "-ZXII", | ||
| [1, 0, 2, 3], | ||
| False, | ||
| ), | ||
| # same Paulis and qubits | ||
| ("XYIZ", [1, 0, 2, 3], "XYIZ", [1, 0, 2, 3], True), | ||
| # same Paulis and qubits | ||
| ("-XYIZ", [1, 0, 2, 3], "-XYIZ", [1, 0, 2, 3], True), | ||
| # same Paulis and qubits up to reordering | ||
| ( | ||
| "XXIY", | ||
| [0, 1, 2, 3], | ||
| "YIXX", | ||
| [3, 2, 1, 0], | ||
| True, | ||
| ), | ||
| # same Paulis and qubits up to reordering | ||
| ( | ||
| "XXIY", | ||
| [0, 1, 2, 3], | ||
| "XXIY", | ||
| [0, 1, 3, 2], | ||
| True, | ||
| ), | ||
| # same Paulis and qubits up to reordering | ||
| ( | ||
| "-XXIY", | ||
| [0, 1, 2, 3], | ||
| "-YIXX", | ||
| [2, 3, 1, 0], | ||
| True, | ||
| ), | ||
| ] | ||
|
|
||
| for pauli1, qubits1, pauli2, qubits2, expected in cases: | ||
| with self.subTest(pauli1=pauli1, qubits1=qubits1, pauli2=pauli2, qubits2=qubits2): | ||
| ppm1 = build_pauli_gate(pauli1, "measure") | ||
| ppm2 = build_pauli_gate(pauli2, "measure") | ||
| self.assertEqual(scc.commute(ppm1, qubits1, [0], ppm2, qubits2, [0]), expected) | ||
|
|
||
| def test_pauli_evolution_sums(self): | ||
| """Test PauliEvolutionGate commutations for operators that are sums of Paulis.""" | ||
|
|
@@ -585,7 +645,8 @@ def test_pauli_and_standard_gate(self, pauli_type): | |
| """Test Pauli-based gates and standard gate commutations are efficiently supported.""" | ||
| # 40-qubit Pauli gate with following terms: X: 0-9, Y: 10-19, Z: 20-29, I: 30-39 | ||
| pauli = 10 * "I" + 10 * "Z" + 10 * "Y" + 10 * "X" | ||
| pauli_indices = list(range(len(pauli))) | ||
| pauli_qubits = list(range(len(pauli))) | ||
| pauli_clbits = [0] if pauli_type == "measure" else [] | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Heh I stumbled upon this as well in another commit and was wondering how this passed before
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that's because we never really checked clbits in (this part of) commutation checker. |
||
| pauli_gate = build_pauli_gate(pauli, pauli_type) | ||
|
|
||
| # Test cases in the format: (gate, indices, commutes) | ||
|
|
@@ -607,7 +668,9 @@ def test_pauli_and_standard_gate(self, pauli_type): | |
|
|
||
| for std_gate, indices, expected in cases: | ||
| with self.subTest(std_gate=std_gate, indices=indices): | ||
| commutes = scc.commute(pauli_gate, pauli_indices, [], std_gate, indices, []) | ||
| commutes = scc.commute( | ||
| pauli_gate, pauli_qubits, pauli_clbits, std_gate, indices, [] | ||
| ) | ||
| self.assertEqual(expected, commutes) | ||
|
|
||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could just add the sign into
try_extract_op_from_ppmand avoid duplicating the function -- technically it would be correct to include the sign there and it won't affect any other commutation check.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean including the sign into the
SparseObservableitself? I actually thought of doing this, but then preferred not to change the existing code too much risking to insert more bugs. This is a temporary function anyway in view of #15815.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With a open follow-up I'm fine, otherwise our "temporary" code tends to be eternal 🙂