-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Improve commutation checking of Pauli product rotations and measurements #15815
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
Changes from 6 commits
5d2d7d0
dff5668
49960e1
0dde194
4cd1ff4
789e1c6
de13108
087362f
08f9fa5
1beefa4
e45c48a
6cbce16
b654130
d889df0
06cbb55
f4a9df6
b13a58d
76a8c74
55e93d1
c90835c
b3e6456
a685517
9f7a432
fb03d53
c2c3ca5
a669772
a67a060
d13a51a
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 |
|---|---|---|
|
|
@@ -234,7 +234,7 @@ fn try_extract_op_from_ppr( | |
| Some(out.compose_map(&local, |i| qubits[i as usize].0)) | ||
| } | ||
|
|
||
| fn try_pauli_generator( | ||
| fn try_sparse_observable_generator( | ||
| operation: &OperationRef, | ||
| qubits: &[Qubit], | ||
| num_qubits: u32, | ||
|
|
@@ -248,6 +248,16 @@ fn try_pauli_generator( | |
| } | ||
| } | ||
|
|
||
| /// Checks if the generator can be represented as a single Pauli term. | ||
| /// If so, returns the generator in the (Z, X) form. | ||
| fn try_pauli_generator<'a>(operation: &'a OperationRef) -> Option<(&'a Vec<bool>, &'a Vec<bool>)> { | ||
| match operation { | ||
| OperationRef::PauliProductRotation(ppr) => Some((&ppr.z, &ppr.x)), | ||
| OperationRef::PauliProductMeasurement(ppm) => Some((&ppm.z, &ppm.x)), | ||
| _ => None, | ||
| } | ||
| } | ||
|
|
||
| fn get_bits_from_py<T>( | ||
| py_bits1: &Bound<'_, PyTuple>, | ||
| py_bits2: &Bound<'_, PyTuple>, | ||
|
|
@@ -521,10 +531,27 @@ impl CommutationChecker { | |
| _ => (), | ||
| }; | ||
|
|
||
| if let (Some((z1, x1)), Some((z2, x2))) = | ||
| (try_pauli_generator(op1), try_pauli_generator(op2)) | ||
| { | ||
| let max_q1 = qargs1.iter().map(|q| q.index()).max().unwrap_or(0); | ||
|
Member
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. Why is the fallback value for
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. I have replaced this code with one using |
||
| let mut in_q1 = vec![usize::MAX; max_q1 + 1]; | ||
| for (i, &q) in qargs1.iter().enumerate() { | ||
| in_q1[q.index()] = i; | ||
| } | ||
|
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. I think we discussed this offline but I don't remember the answer: would it be faster to sort both qargs (log timing) and then iterate, rather than finding the max (linear)?
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. I tried this now and it became 30% slower, see #15815 for how the experiments were run.
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. I agree that for robustness it's best to avoid linear scaling with the total number of qubits in the circuit, so I changed the implementation to use a
Cryoris marked this conversation as resolved.
|
||
| let mut parity = false; | ||
| for (j, &q) in qargs2.iter().enumerate() { | ||
| if let Some(&i) = in_q1.get(q.index()).filter(|&&i| i != usize::MAX) { | ||
| parity ^= (x1[i] && z2[j]) ^ (z1[i] && x2[j]); | ||
| } | ||
| } | ||
| return Ok(!parity); | ||
| } | ||
|
|
||
| // Handle commutations in between Pauli-based gates, like PauliGate or PauliEvolutionGate | ||
| let size = qargs1.iter().chain(qargs2.iter()).max().unwrap().0 + 1; | ||
| if let Some(obs1) = try_pauli_generator(op1, qargs1, size) { | ||
| if let Some(obs2) = try_pauli_generator(op2, qargs2, size) { | ||
| if let Some(obs1) = try_sparse_observable_generator(op1, qargs1, size) { | ||
| if let Some(obs2) = try_sparse_observable_generator(op2, qargs2, size) { | ||
| return Ok(obs1.commutes(&obs2, tol)); | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| --- | ||
| features_transpiler: | ||
| - Improved the performance of commutation checking for :class:`.PauliProductRotationGate` and | ||
| :class:`.PauliProductMeasurement`. | ||
|
Member
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. Would we like to mention here very briefly as to what has improved to improve the performance of the commutation checker?
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. Thanks for the great feedback, Janani! I have addressed and your other review comments in b13a58d. |
||
Uh oh!
There was an error while loading. Please reload this page.