-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Extend litinski transformation for all Pauli rotations #15974
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
6d85e81
4495a29
48c341f
7c77ed5
1b7629b
10baa04
025622f
27c53d8
6e4148d
fd9ff5e
e368971
b3d5602
778d552
699cd4f
284ee64
d744ad3
0bb2ec5
2170be0
ab3599f
d1a354a
e6b121d
a0cb601
450c0b8
778bf3b
99a7827
0506fe1
57c4cdd
24f60a9
8dcb860
4d4b886
ec9795a
0a38e2c
de36c3d
ced111f
3036ed9
d33977c
479194d
3569230
c62bb18
ba4bdc0
5dfdeea
519cfba
1b5c7bf
12423db
80f6640
457dcc2
872098b
f6068d1
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 |
|---|---|---|
|
|
@@ -9,6 +9,7 @@ | |
| // Any modifications or derivative works of this code must retain this | ||
| // copyright notice, and modified files need to carry a notice indicating | ||
| // that they have been altered from the originals. | ||
| use itertools::Itertools; | ||
| use std::fmt; | ||
|
|
||
| use fixedbitset::FixedBitSet; | ||
|
|
@@ -287,19 +288,144 @@ impl Clifford { | |
| &mut self.scratch, | ||
| ); | ||
| } | ||
| /// Modifies the tableau in-place by appending RZ-gate, | ||
| /// with an angle that is an integer multiple of pi/2 | ||
| pub fn append_rz(&mut self, qubit: usize, multiple: usize) { | ||
| let multiple = multiple.rem_euclid(4); | ||
| match multiple { | ||
| 0 => {} | ||
| 1 => self.append_s(qubit), | ||
| 2 => self.append_z(qubit), | ||
| 3 => self.append_sdg(qubit), | ||
| _ => unreachable!("RZ is only applicable for multiples of pi/2 rotations."), | ||
|
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. If we have this function as a part of public API, we should be able to call it with any integer multiple of pi/2, not necessarily reduced modulo 4. The change is to match
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. done in 1b5c7bf |
||
| } | ||
| } | ||
| /// Modifies the tableau in-place by appending RX-gate, | ||
| /// with an angle that is an integer multiple of pi/2 | ||
| pub fn append_rx(&mut self, qubit: usize, multiple: usize) { | ||
| let multiple = multiple.rem_euclid(4); | ||
| match multiple { | ||
| 0 => {} | ||
| 1 => self.append_sx(qubit), | ||
| 2 => self.append_x(qubit), | ||
| 3 => self.append_sxdg(qubit), | ||
| _ => unreachable!("RX is only applicable for multiples of pi/2 rotations."), | ||
| } | ||
| } | ||
| /// Modifies the tableau in-place by appending RY-gate, | ||
| /// with an angle that is an integer multiple of pi/2 | ||
| pub fn append_ry(&mut self, qubit: usize, multiple: usize) { | ||
| let multiple = multiple.rem_euclid(4); | ||
| match multiple { | ||
| 0 => {} | ||
| 1 => { | ||
| self.append_z(qubit); | ||
| self.append_h(qubit) | ||
| } | ||
| 2 => self.append_y(qubit), | ||
| 3 => { | ||
| self.append_h(qubit); | ||
| self.append_z(qubit) | ||
| } | ||
| _ => unreachable!("RY is only applicable for multiples of pi/2 rotations."), | ||
| } | ||
| } | ||
| /// Modifies the tableau in-place by appending PPR gate, | ||
| /// with an angle that is an integer multiple of pi/2 | ||
| pub fn append_ppr( | ||
|
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. Should we add some rust tests 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. Added a test here: 519cfba
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" terms are handled here (including tests): f6068d1 |
||
| &mut self, | ||
| pauli_z: &[bool], | ||
| pauli_x: &[bool], | ||
| indices: &[u32], | ||
| multiple: usize, | ||
| ) { | ||
| let multiple = multiple.rem_euclid(4); | ||
|
|
||
| // remove pauli I terms | ||
| let (new_z, new_x, new_indices): (Vec<bool>, Vec<bool>, Vec<u32>) = pauli_z | ||
| .iter() | ||
| .zip(pauli_x) | ||
| .zip(indices) | ||
| .filter(|&((&z, &x), _)| z || x) | ||
| .map(|((&z, &x), &i)| (z, x, i)) | ||
| .multiunzip(); | ||
|
|
||
| // initial H or SX gates (in case of pauli X or pauli Y respectively) | ||
| for qubit in 0..new_indices.len() { | ||
| match (new_z[qubit], new_x[qubit]) { | ||
| (true, false) => {} // pauli Z on qubit | ||
| (true, true) => self.append_sx(new_indices[qubit] as usize), // pauli Y on qubit | ||
| (false, true) => self.append_h(new_indices[qubit] as usize), // pauli X on qubit | ||
| (false, false) => unreachable!("Pauli I terms were removed from PPR."), // pauli I on qubit (shouldn't get it since pauli is sparse) | ||
| } | ||
| } | ||
|
|
||
| /// Evolving the single-qubit Pauli-Z with Z on qubit qbit. | ||
| // CX ladder | ||
| if new_indices.len() > 1 { | ||
| for ind in (0..new_indices.len() - 1).rev() { | ||
| self.append_cx(new_indices[ind + 1] as usize, new_indices[ind] as usize); | ||
| } | ||
| } | ||
| // internal RZ gate | ||
| match multiple { | ||
| 0 => {} | ||
| 1 => self.append_s(new_indices[0] as usize), | ||
| 2 => self.append_z(new_indices[0] as usize), | ||
| 3 => self.append_sdg(new_indices[0] as usize), | ||
| _ => unreachable!("PPR is only applicable for multiples of pi/2 rotations."), | ||
| } | ||
| // CX ladder | ||
| if new_indices.len() > 1 { | ||
| for ind in 0..new_indices.len() - 1 { | ||
| self.append_cx(new_indices[ind + 1] as usize, new_indices[ind] as usize); | ||
| } | ||
| } | ||
| // final H or SXdg gates (in case of pauli X or pauli Y respectively) | ||
| for qubit in 0..new_indices.len() { | ||
| match (new_z[qubit], new_x[qubit]) { | ||
| (true, false) => {} // pauli Z on qubit | ||
| (true, true) => self.append_sxdg(new_indices[qubit] as usize), // pauli Y on qubit | ||
| (false, true) => self.append_h(new_indices[qubit] as usize), // pauli X on qubit | ||
| (false, false) => unreachable!("Pauli I terms were removed from PPR."), // pauli I on qubit (shouldn't get it since pauli is sparse) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// Evolving a single qubit pauli on qubit qbit by the Clifford. | ||
| /// The pauli (X, Y or Z) is given as (pauli_z, pauli_x) | ||
| /// Returns the evolved Pauli in the a sparse ZX format: (sign, z, x, indices). | ||
| pub fn get_inverse_z(&self, qbit: usize) -> (bool, Vec<bool>, Vec<bool>, Vec<u32>) { | ||
| pub fn evolve_single_qubit_pauli( | ||
| &self, | ||
| pauli_z: bool, | ||
| pauli_x: bool, | ||
| qbit: usize, | ||
| ) -> (bool, Vec<bool>, Vec<bool>, Vec<u32>) { | ||
| let mut z = Vec::with_capacity(self.num_qubits); | ||
| let mut x = Vec::with_capacity(self.num_qubits); | ||
| let mut indices = Vec::with_capacity(self.num_qubits); | ||
| let mut pauli_indices = Vec::<usize>::with_capacity(2 * self.num_qubits); | ||
| // Compute the y-count to avoid recomputing it later | ||
| let mut pauli_y_count: u32 = 0; | ||
| for i in 0..self.num_qubits { | ||
| let z_bit = self.tableau[qbit][i]; | ||
| let x_bit = self.tableau[qbit][i + self.num_qubits]; | ||
| let (z_bit, x_bit) = match (pauli_z, pauli_x) { | ||
| (true, false) => ( | ||
| // pauli Z | ||
| self.tableau[qbit][i], | ||
| self.tableau[qbit][i + self.num_qubits], | ||
| ), | ||
| (false, true) => ( | ||
| // pauli X | ||
| self.tableau[qbit + self.num_qubits][i], | ||
| self.tableau[qbit + self.num_qubits][i + self.num_qubits], | ||
| ), | ||
| (true, true) => ( | ||
| // pauli Y | ||
| self.tableau[qbit + self.num_qubits][i] ^ self.tableau[qbit][i], | ||
| self.tableau[qbit + self.num_qubits][i + self.num_qubits] | ||
| ^ self.tableau[qbit][i + self.num_qubits], | ||
| ), | ||
| _ => unreachable!("This is only called for RX/RZ/RY gates."), | ||
| }; | ||
| if z_bit || x_bit { | ||
| z.push(z_bit); | ||
| x.push(x_bit); | ||
|
|
||
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.
Nitpick about the naming. Seeing the function
append_rzdefined for a Clifford class is confusing, as appending an RZ gate to a Clifford generally no longer results in a Clifford. Moreover, RZ gates correspond to rotation angles, not "multiples". Maybe you can call thisappend_multiple_of_sorappend_clifford_rz. The same comment applies to other new functions in this file.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 have a similar function in the python clifford class:
qiskit/qiskit/quantum_info/operators/symplectic/clifford_circuits.py
Line 351 in 2be7c41