-
Notifications
You must be signed in to change notification settings - Fork 3k
Add additional Z and X rotation gate handling to CommutativeCancellation #16191
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 all commits
81057d1
82f78f0
808e014
2cd0983
3f44612
db2b040
5233675
eb0ee64
d810476
54fd4ca
b9a00e5
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 |
|---|---|---|
|
|
@@ -21,6 +21,7 @@ use smallvec::{SmallVec, smallvec}; | |
|
|
||
| use super::analyze_commutations; | ||
| use crate::commutation_checker::CommutationChecker; | ||
| use approx::abs_diff_eq; | ||
| use qiskit_circuit::Qubit; | ||
| use qiskit_circuit::dag_circuit::{DAGCircuit, NodeType}; | ||
| use qiskit_circuit::operations::{Operation, Param, StandardGate}; | ||
|
|
@@ -34,15 +35,22 @@ static VAR_Z_MAP: [(&str, StandardGate); 3] = [ | |
| ("p", StandardGate::Phase), | ||
| ("u1", StandardGate::U1), | ||
| ]; | ||
| static Z_ROTATIONS: [StandardGate; 6] = [ | ||
| static Z_ROTATIONS: [StandardGate; 8] = [ | ||
| StandardGate::Phase, | ||
| StandardGate::Z, | ||
| StandardGate::U1, | ||
| StandardGate::RZ, | ||
| StandardGate::T, | ||
| StandardGate::Tdg, | ||
| StandardGate::S, | ||
| StandardGate::Sdg, | ||
| ]; | ||
| static X_ROTATIONS: [StandardGate; 4] = [ | ||
| StandardGate::X, | ||
| StandardGate::RX, | ||
| StandardGate::SX, | ||
| StandardGate::SXdg, | ||
| ]; | ||
|
ShellyGarion marked this conversation as resolved.
|
||
| static X_ROTATIONS: [StandardGate; 2] = [StandardGate::X, StandardGate::RX]; | ||
| static SUPPORTED_GATES: [StandardGate; 5] = [ | ||
| StandardGate::CX, | ||
| StandardGate::CY, | ||
|
|
@@ -92,6 +100,8 @@ pub fn cancel_commutations( | |
| .map(|(_, gate)| gate) | ||
| }) | ||
| }); | ||
| let sx_supported = dag.get_op_counts().contains_key("sx") || basis.iter().any(|x| x == "sx"); | ||
|
Contributor
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. General question: do we only run CC before routing? If no we'd have to take into account whether the wire contains SX/X instead of the DAG in general
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. We run it both before and after routing (at optimization level 2 and 3). The pass is not target aware right now at all. It doesn't understand heterogeneous gate sets or per qubit requirements. Even though the pass takes a target it in reduces that to a global gate name list and then acts upon it here. We can work on making the pass truly target aware but I think that should be a dedicated PR because it probably will need a larger refactor than what this is adding. I was also hesitant to work on this pass too much if we're planning to replace it with |
||
| let x_supported = dag.get_op_counts().contains_key("x") || basis.iter().any(|x| x == "x"); | ||
|
|
||
| // RZ and P/U1 have a phase difference of angle/2, which we need to account for | ||
| let z_phase_shift = match z_var_gate { | ||
|
|
@@ -247,9 +257,13 @@ pub fn cancel_commutations( | |
| } else { | ||
| match node_op_name { | ||
| "t" => Ok((FRAC_PI_4, z_phase_shift("p", FRAC_PI_4))), | ||
| "tdg" => Ok((-FRAC_PI_4, -z_phase_shift("p", FRAC_PI_4))), | ||
| "s" => Ok((FRAC_PI_2, z_phase_shift("p", FRAC_PI_2))), | ||
| "sdg" => Ok((-FRAC_PI_2, -z_phase_shift("p", FRAC_PI_2))), | ||
| "z" => Ok((PI, z_phase_shift("p", PI))), | ||
| "x" => Ok((PI, FRAC_PI_2)), | ||
| "sx" => Ok((FRAC_PI_2, FRAC_PI_4)), | ||
| "sxdg" => Ok((-FRAC_PI_2, -FRAC_PI_4)), | ||
| _ => Err(PyRuntimeError::new_err(format!( | ||
| "Angle for operation {node_op_name} is not defined" | ||
| ))), | ||
|
|
@@ -259,24 +273,34 @@ pub fn cancel_commutations( | |
| total_phase += phase_shift; | ||
| } | ||
|
|
||
| let new_op = match cancel_key.gate { | ||
| GateOrRotation::ZRotation => z_var_gate.unwrap(), | ||
| GateOrRotation::XRotation => &StandardGate::RX, | ||
| _ => unreachable!(), | ||
| }; | ||
|
|
||
| let pi_multiple = total_angle / PI; | ||
|
|
||
| let mod4 = pi_multiple.rem_euclid(4.); | ||
| if mod4 < _CUTOFF_PRECISION || (4. - mod4) < _CUTOFF_PRECISION { | ||
| if is_multiple_of_pi(total_angle, 4.) { | ||
| // if the angle is close to a 4-pi multiple (from above or below), then the | ||
| // operator is equal to the identity | ||
| } else if (mod4 - 2.).abs() < _CUTOFF_PRECISION { | ||
| } else if is_multiple_of_pi(total_angle, 2.) { | ||
| // a 2-pi multiple has a phase of pi: RX(2pi) = RZ(2pi) = -I = I exp(i pi) | ||
| total_phase -= PI; | ||
| } else if cancel_key.gate == GateOrRotation::ZRotation { | ||
| let z_gate = z_var_gate.unwrap(); | ||
| dag.insert_1q_on_incoming_qubit((*z_gate, &[total_angle]), cancel_set[0]); | ||
| } else { | ||
| // any other is not the identity and we add the gate | ||
| dag.insert_1q_on_incoming_qubit((*new_op, &[total_angle]), cancel_set[0]); | ||
| // cancel_gate.key is either ZRotation or XRotation in this block it is an | ||
| // XRotation. | ||
| if x_supported && is_multiple_of_pi(total_angle, 1.) { | ||
| let num_x = (total_angle / PI).round(); | ||
| total_phase -= FRAC_PI_2 * num_x; | ||
| dag.insert_1q_on_incoming_qubit((StandardGate::X, &[]), cancel_set[0]); | ||
| } else if sx_supported && is_multiple_of_pi(total_angle, 0.5) { | ||
| let num_sx = (total_angle / FRAC_PI_2).round(); | ||
| total_phase -= FRAC_PI_4 * num_sx; | ||
| for _ in 0..(num_sx as i64) % 4 { | ||
| dag.insert_1q_on_incoming_qubit((StandardGate::SX, &[]), cancel_set[0]); | ||
| } | ||
| } else { | ||
| dag.insert_1q_on_incoming_qubit( | ||
| (StandardGate::RX, &[total_angle]), | ||
| cancel_set[0], | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| dag.add_global_phase(&Param::Float(total_phase))?; | ||
|
|
@@ -291,6 +315,14 @@ pub fn cancel_commutations( | |
| Ok(()) | ||
| } | ||
|
|
||
| /// Checks if angle is an integer multiple of ``factor * PI``. | ||
| fn is_multiple_of_pi(angle: f64, factor: f64) -> bool { | ||
| let modulo = angle / (factor * PI); | ||
| let remainder = modulo.rem_euclid(1.0); | ||
| abs_diff_eq!(remainder, 0., epsilon = _CUTOFF_PRECISION) | ||
| || abs_diff_eq!(remainder, 1., epsilon = _CUTOFF_PRECISION) | ||
| } | ||
|
|
||
| pub fn commutation_cancellation_mod(m: &Bound<PyModule>) -> PyResult<()> { | ||
| m.add_wrapped(wrap_pyfunction!(cancel_commutations))?; | ||
| Ok(()) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| --- | ||
| features_transpiler: | ||
| - The :class:`.CommutativeCancellation` transpiler pass will now emit an | ||
| :class:`.XGate` or :class:`.SXGate` if the tracked X rotation is an angle | ||
| of $\pi$ or a multiple of $\frac{\pi}{2}$ respectively and either the | ||
| :class:`.Target` supports class:`.XGate` or :class:`.SXGate`, or the | ||
| circuit contains the gates. |
Uh oh!
There was an error while loading. Please reload this page.