-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
Use average gate fidelity in the commutation checker #13874
Conversation
…kit#13417)" This reverts commit b9d5c9c.
One or more of the following people are relevant to this code:
|
This currently fails because the error tolerance for the matrix-based checks is too low. This leads e.g. to the matrix-based check of |
these cover cases where we accumulate roundoff errors
- increased the threshold for value comparisons to 16 EPS - use same functions in RemoveIdentityEquiv and CC checker - add reno - add detailed docs - add some tests for above thresholds
Pull Request Test Coverage Report for Build 13687167568Warning: This coverage report may be inaccurate.This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.
Details
💛 - Coveralls |
which is consistent with the 2qWeylDecomp and still stricter than Qiskit 1.x
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.
Overall this LGTM, it'll be good to have everything consistent. Just a few inline comments.
// If the average gate infidelity is below this tolerance, they commute. The tolerance | ||
// is set to max(1e-12, 1 - approximation_degree), to account for roundoffs and for | ||
// consistency with other places in Qiskit. | ||
let tol = 1e-12_f64.max(1. - approximation_degree); |
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.
This does mean we can't approximate more than what a 1e-12 tolerance provides. But it's the same logic we use in other places so I think that's fine.
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.
Yeah it is unfortunately. I don't like this very much either, but currently approximation_degree
is 1.
everywhere per default which doesn't really allow us to set another default like 1-1e-12
. The other option would've been to use None
as indicator, but that already means that the target
error rates should be used.
let dim = match rotation { | ||
"rx" | "ry" | "rz" | "p" | "u1" => 2., | ||
_ => 4., | ||
}; | ||
|
||
let trace_over_dim = match rotation { | ||
"rx" | "ry" | "rz" | "rxx" | "ryy" | "rzx" | "rzz" => { | ||
Complex64::new((angle / 2.).cos(), 0.) | ||
} | ||
"crx" | "cry" | "crz" => Complex64::new(0.5 + 0.5 * (angle / 2.).cos(), 0.), | ||
"p" | "u1" => (1. + Complex64::new(0., angle).exp()) / 2., | ||
"cp" => (3. + Complex64::new(0., angle).exp()) / 4., | ||
_ => return None, |
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.
I feel like we could do this without the strings and used the StandardGate
enum variants from standard gates. It would be faster to do the enum match because it's a u8 vs a string comparison.
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.
I just saw this is used elsewhere in accelerate. I feel like this comment is especially true if we're going to use this for standardized trace computation for standard gates.
@@ -14,6 +14,7 @@ use num_complex::ComplexFloat; | |||
use pyo3::prelude::*; | |||
use rustworkx_core::petgraph::stable_graph::NodeIndex; | |||
|
|||
use crate::commutation_checker::rotation_trace_and_dim; |
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 think we should put this somewhere more central?
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.
Yeah I was looking for a more central place but didn't find an existing one that suited well.. and I didn't want to create a new one just with this file. Maybe we can just create a gate_metrics.rs
or something that also includes the gate_fidelity
function
@@ -33,12 +34,15 @@ fn remove_identity_equiv( | |||
) { | |||
let mut remove_list: Vec<NodeIndex> = Vec::new(); | |||
let mut global_phase_update: f64 = 0.; | |||
// Minimum threshold to compare average gate fidelity to 1. This is chosen to account | |||
// for roundoff errors and to be consistent with other places. | |||
let minimum_tol = 1e-12_f64; |
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.
I'd make this a const
:
let minimum_tol = 1e-12_f64; | |
const MINIMUM_TOL: f64 = 1e-12; |
Although you might have to move it outside the function definition to do this.
let name = gate.name(); | ||
if let Param::Float(angle) = inst.params_view()[0] { | ||
let (tr_over_dim, dim) = | ||
rotation_trace_and_dim(name, angle).expect("All rotation covered"); |
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.
Since this becomes a panic message maybe something a bit more descriptive/clear. We should never encounter it because the match set is complete but since you went to the trouble of adding a message instead of just unwrap()
we should provide a bit more detail.
let mut out = arr2(&[ | ||
[C_ZERO, C_ZERO, C_ZERO, C_ZERO], | ||
[C_ZERO, C_ZERO, C_ZERO, C_ZERO], | ||
[C_ZERO, C_ZERO, C_ZERO, C_ZERO], | ||
[C_ZERO, C_ZERO, C_ZERO, C_ZERO], | ||
]); |
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.
Is there a reason to not do?:
let mut out = arr2(&[ | |
[C_ZERO, C_ZERO, C_ZERO, C_ZERO], | |
[C_ZERO, C_ZERO, C_ZERO, C_ZERO], | |
[C_ZERO, C_ZERO, C_ZERO, C_ZERO], | |
[C_ZERO, C_ZERO, C_ZERO, C_ZERO], | |
]); | |
let mut out = Array2::zeros((4, 4)); |
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.
There's zero reason not to 😉
/// | ||
/// This is analogous to NumPy's ``allclose`` function. | ||
pub fn allclose( | ||
pub fn mm2q( |
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.
Maybe write this out as matmul_2q
or something a bit more descriptive? It took me a sec to understand what this meant at first. Or just a docstring to say what it is.
} | ||
} | ||
true | ||
fn mm1q(left: &ArrayView2<Complex64>, right: &ArrayView2<Complex64>) -> Array2<Complex64> { |
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.
Maybe write this out as matmul_1q or something a bit more descriptive? It took me a sec to understand what this meant at first then I looked at the code. Or just a docstring to say what it is.
There is also an existing function for this here: https://github.com/Qiskit/qiskit/blob/main/crates/accelerate/src/euler_one_qubit_decomposer.rs#L1233-L1245 but it works with slightly different types.
qiskit/circuit/__init__.py
Outdated
.. autosummary:: | ||
:toctree: ../stubs/ | ||
|
||
CommutationChecker |
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.
Isn't the CommutationChecker
part of the public API we probably shouldn't remove this. (we had this exact problem as part of 1.1 or something where somebody removed it from the docs)
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.
huh the page doesn't exist on the 1.4 docs (which is probably why I removed it here), but it does exist on the 2.0 dev doc -- I'll add it again, thanks for the check!
@@ -0,0 +1,24 @@ | |||
--- | |||
upgrade: |
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.
You need a feature note about the new argument on the commutative pass constructors.
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.
in my mind it was not API documented 😛 I'll add it!
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.
Thanks for the quick updates. There are 3 small nits inline otherwise I think this is ready to go. I left one larger comment inline about to rewrite the rotation gate handling without using a string. But we can save that for a follow up, it's just a pet peeve of mine and is arguably scope creep for this PR.
let dim = left.nrows(); // == left.ncols() == right.nrows() == right.ncols() | ||
// let trace = left.t().mapv(|el| el.conj()).dot(right).diag().sum(); |
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.
Are these code comments intentional?
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.
The first lines yes, the second no 😄 but also the first line is not really necessary, I removed them both
@@ -652,13 +605,19 @@ fn map_rotation<'a>( | |||
) -> (&'a OperationRef<'a>, &'a [Param], bool) { | |||
let name = op.name(); | |||
|
|||
if let Some((pi_multiple, generator)) = SUPPORTED_ROTATIONS.get(name) { | |||
if let Some(generator) = SUPPORTED_ROTATIONS.get(name) { |
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.
Maybe not for this PR but we can drop the hashset here all together and have a static lookup table based on the standard gates. Something like:
const fn builld_lut() -> [Option<StandardGate>; STANDARD_GATE_SIZE] {
...
}
let static supported_rotations: [Option<StandardGate>; STANDARD_GATE_SIZE] = build_lut()
Alternatively you could just do a couple of if matches!()
on .standard_gate()
.
When in rust space and working solely with standard gates we really never have a need for strings, so this just sticks out to me every time I see it. But it was pre-existing so we can do this in a follow up.
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.
Yeah that sounds like a good idea, I'll put it on the follow-up list if that's good for you 🙂
Summary
The commutation checker currently uses a different metric to check when gates commute than other places in Qiskit, such as the
RemoveIdentityEquivalent
. This leads to strange behavior, where one gate is treated as identity in the commutation, but is subsequently not removed as other passes do not treat it as identity. This is what causes #13547.This PR updates the commutation checker to use the same average gate fidelity and thus the transpiler to handle almost-identity gates more consistently.
Closes #13547.
Details and comments
Here's a small benchmark comparing transpile time, CX depth and count for the (1) the QFT, (2) an efficient SU2 circuit with log(num qubits) depth and (3) a Heisenberg Trotter evolution on a line.