paulistrings/channel/ptm/
clifford.rs1use super::{assert_distinct, PtmChannel, PtmKind, PtmStorage};
4use crate::channel::prepared::LOCAL_DIM;
5use crate::channel::{read_pauli, write_pauli};
6use crate::pauli_string::PauliString;
7use crate::phase::Phase;
8
9impl PtmChannel {
10 pub fn hadamard(qubit: usize) -> Self {
12 Self::clifford_1q(qubit, [0, 2, 1, 3], 0b1000, PtmKind::Hadamard)
13 }
14
15 pub fn phase_gate(qubit: usize) -> Self {
17 Self::clifford_1q(qubit, [0, 3, 2, 1], 0b1000, PtmKind::PhaseGate)
18 }
19
20 pub fn pauli_x(qubit: usize) -> Self {
22 Self::clifford_1q(qubit, [0, 1, 2, 3], 0b1100, PtmKind::PauliX)
23 }
24
25 pub fn pauli_y(qubit: usize) -> Self {
27 Self::clifford_1q(qubit, [0, 1, 2, 3], 0b0110, PtmKind::PauliY)
28 }
29
30 pub fn pauli_z(qubit: usize) -> Self {
32 Self::clifford_1q(qubit, [0, 1, 2, 3], 0b1010, PtmKind::PauliZ)
33 }
34
35 pub fn cnot(control: usize, target: usize) -> Self {
41 assert_distinct("cnot", control, target);
42 Self::clifford_2q(
43 [control, target],
44 PtmKind::Cnot,
45 [pack(1, 0, 1, 0), pack(0, 1, 0, 0), pack(0, 0, 1, 0), pack(0, 1, 0, 1)],
46 )
47 }
48
49 pub fn cz(q0: usize, q1: usize) -> Self {
55 assert_distinct("cz", q0, q1);
56 Self::clifford_2q(
57 [q0, q1],
58 PtmKind::Cz,
59 [pack(1, 0, 0, 1), pack(0, 1, 0, 0), pack(0, 1, 1, 0), pack(0, 0, 0, 1)],
60 )
61 }
62
63 pub fn swap(q0: usize, q1: usize) -> Self {
69 assert_distinct("swap", q0, q1);
70 Self::clifford_2q(
71 [q0, q1],
72 PtmKind::Swap,
73 [pack(0, 0, 1, 0), pack(0, 0, 0, 1), pack(1, 0, 0, 0), pack(0, 1, 0, 0)],
74 )
75 }
76
77 fn clifford_1q(qubit: usize, image: [u8; 4], negated: u16, kind: PtmKind) -> Self {
78 let mut padded = [0u8; LOCAL_DIM];
79 padded[..4].copy_from_slice(&image);
80 Self::new(&[qubit], PtmStorage::SignedPermutation { image: padded, negated }, kind)
81 }
82
83 fn clifford_2q(qubits: [usize; 2], kind: PtmKind, generator_images: [u8; 4]) -> Self {
85 let mut image = [0u8; LOCAL_DIM];
86 let mut negated = 0u16;
87 for (index, slot) in image.iter_mut().enumerate() {
88 let mut product = PauliString::<1>::identity();
90 let mut phase = Phase::ONE;
91 for (generator, &generator_image) in generator_images.iter().enumerate() {
92 if (index >> generator) & 1 == 1 {
93 phase += product.mul_assign(&unpack(generator_image));
94 }
95 }
96 let y_count =
97 ((index & 1) & ((index >> 1) & 1)) + (((index >> 2) & 1) & ((index >> 3) & 1));
98 phase += Phase::new(y_count as u8);
99 debug_assert!(
100 phase == Phase::ONE || phase == Phase::MINUS_ONE,
101 "a Clifford maps a Hermitian Pauli to ± a Hermitian Pauli"
102 );
103 *slot = (read_pauli(&product.x, &product.z, 0, 0)
104 | (read_pauli(&product.x, &product.z, 0, 1) << 2)) as u8;
105 if phase == Phase::MINUS_ONE {
106 negated |= 1 << index;
107 }
108 }
109 Self::new(&qubits, PtmStorage::SignedPermutation { image, negated }, kind)
110 }
111}
112
113const fn pack(x0: u8, z0: u8, x1: u8, z1: u8) -> u8 {
115 x0 | (z0 << 1) | (x1 << 2) | (z1 << 3)
116}
117
118fn unpack(packed: u8) -> PauliString<1> {
120 let mut string = PauliString::<1>::identity();
121 write_pauli(&mut string.x, &mut string.z, 0, 0, 1, (packed & 3) as usize);
122 write_pauli(&mut string.x, &mut string.z, 0, 1, 2, ((packed >> 2) & 3) as usize);
123 string
124}