GCC Code Coverage Report


Directory: ./
File: PauliGraph/ConjugatePauliFunctions.cpp
Date: 2022-10-15 05:10:18
Warnings: 2 unchecked decisions!
Exec Total Coverage
Lines: 40 47 85.1%
Functions: 4 4 100.0%
Branches: 63 118 53.4%
Decisions: 16 22 72.7%

Line Branch Decision Exec Source
1 // Copyright 2019-2022 Cambridge Quantum Computing
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "ConjugatePauliFunctions.hpp"
16
17 #include <tkassert/Assert.hpp>
18
19 #include "OpType/OpTypeInfo.hpp"
20 #include "PauliGraph/PauliGraph.hpp"
21
22 namespace tket {
23
24 30453 std::pair<Pauli, bool> conjugate_Pauli(OpType op, Pauli p, bool reverse) {
25 static const std::map<std::pair<OpType, Pauli>, std::pair<Pauli, bool>>
26 conj_lut{
27 {{OpType::H, Pauli::I}, {Pauli::I, false}},
28 {{OpType::H, Pauli::X}, {Pauli::Z, false}},
29 {{OpType::H, Pauli::Y}, {Pauli::Y, true}},
30 {{OpType::H, Pauli::Z}, {Pauli::X, false}},
31 {{OpType::S, Pauli::I}, {Pauli::I, false}},
32 {{OpType::S, Pauli::X}, {Pauli::Y, true}},
33 {{OpType::S, Pauli::Y}, {Pauli::X, false}},
34 {{OpType::S, Pauli::Z}, {Pauli::Z, false}},
35 {{OpType::Sdg, Pauli::I}, {Pauli::I, false}},
36 {{OpType::Sdg, Pauli::X}, {Pauli::Y, false}},
37 {{OpType::Sdg, Pauli::Y}, {Pauli::X, true}},
38 {{OpType::Sdg, Pauli::Z}, {Pauli::Z, false}},
39 {{OpType::V, Pauli::I}, {Pauli::I, false}},
40 {{OpType::V, Pauli::X}, {Pauli::X, false}},
41 {{OpType::V, Pauli::Y}, {Pauli::Z, true}},
42 {{OpType::V, Pauli::Z}, {Pauli::Y, false}},
43 {{OpType::Vdg, Pauli::I}, {Pauli::I, false}},
44 {{OpType::Vdg, Pauli::X}, {Pauli::X, false}},
45 {{OpType::Vdg, Pauli::Y}, {Pauli::Z, false}},
46 {{OpType::Vdg, Pauli::Z}, {Pauli::Y, true}},
47 {{OpType::X, Pauli::I}, {Pauli::I, false}},
48 {{OpType::X, Pauli::X}, {Pauli::X, false}},
49 {{OpType::X, Pauli::Y}, {Pauli::Y, true}},
50 {{OpType::X, Pauli::Z}, {Pauli::Z, true}},
51 {{OpType::Y, Pauli::I}, {Pauli::I, false}},
52 {{OpType::Y, Pauli::X}, {Pauli::X, true}},
53 {{OpType::Y, Pauli::Y}, {Pauli::Y, false}},
54 {{OpType::Y, Pauli::Z}, {Pauli::Z, true}},
55 {{OpType::Z, Pauli::I}, {Pauli::I, false}},
56 {{OpType::Z, Pauli::X}, {Pauli::X, true}},
57 {{OpType::Z, Pauli::Y}, {Pauli::Y, true}},
58
4/8
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 30452 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
30453 {{OpType::Z, Pauli::Z}, {Pauli::Z, false}}};
59
60 static const std::map<std::pair<OpType, Pauli>, std::pair<Pauli, bool>>
61 rev_conj_lut{
62 {{OpType::H, Pauli::I}, {Pauli::I, false}},
63 {{OpType::H, Pauli::X}, {Pauli::Z, false}},
64 {{OpType::H, Pauli::Y}, {Pauli::Y, true}},
65 {{OpType::H, Pauli::Z}, {Pauli::X, false}},
66 {{OpType::S, Pauli::I}, {Pauli::I, false}},
67 {{OpType::S, Pauli::X}, {Pauli::Y, false}},
68 {{OpType::S, Pauli::Y}, {Pauli::X, true}},
69 {{OpType::S, Pauli::Z}, {Pauli::Z, false}},
70 {{OpType::Sdg, Pauli::I}, {Pauli::I, false}},
71 {{OpType::Sdg, Pauli::X}, {Pauli::Y, true}},
72 {{OpType::Sdg, Pauli::Y}, {Pauli::X, false}},
73 {{OpType::Sdg, Pauli::Z}, {Pauli::Z, false}},
74 {{OpType::V, Pauli::I}, {Pauli::I, false}},
75 {{OpType::V, Pauli::X}, {Pauli::X, false}},
76 {{OpType::V, Pauli::Y}, {Pauli::Z, false}},
77 {{OpType::V, Pauli::Z}, {Pauli::Y, true}},
78 {{OpType::Vdg, Pauli::I}, {Pauli::I, false}},
79 {{OpType::Vdg, Pauli::X}, {Pauli::X, false}},
80 {{OpType::Vdg, Pauli::Y}, {Pauli::Z, true}},
81 {{OpType::Vdg, Pauli::Z}, {Pauli::Y, false}},
82 {{OpType::X, Pauli::I}, {Pauli::I, false}},
83 {{OpType::X, Pauli::X}, {Pauli::X, false}},
84 {{OpType::X, Pauli::Y}, {Pauli::Y, true}},
85 {{OpType::X, Pauli::Z}, {Pauli::Z, true}},
86 {{OpType::Y, Pauli::I}, {Pauli::I, false}},
87 {{OpType::Y, Pauli::X}, {Pauli::X, true}},
88 {{OpType::Y, Pauli::Y}, {Pauli::Y, false}},
89 {{OpType::Y, Pauli::Z}, {Pauli::Z, true}},
90 {{OpType::Z, Pauli::I}, {Pauli::I, false}},
91 {{OpType::Z, Pauli::X}, {Pauli::X, true}},
92 {{OpType::Z, Pauli::Y}, {Pauli::Y, true}},
93
4/8
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 30452 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
30453 {{OpType::Z, Pauli::Z}, {Pauli::Z, false}}};
94
95
2/2
✓ Branch 0 taken 12953 times.
✓ Branch 1 taken 17500 times.
2/2
✓ Decision 'true' taken 12953 times.
✓ Decision 'false' taken 17500 times.
30453 if (reverse) {
96
1/2
✓ Branch 2 taken 12953 times.
✗ Branch 3 not taken.
12953 return rev_conj_lut.at({op, p});
97 }
98
1/2
✓ Branch 2 taken 17500 times.
✗ Branch 3 not taken.
17500 return conj_lut.at({op, p});
99 }
100
101 814 void conjugate_PauliTensor(
102 QubitPauliTensor& qpt, OpType op, const Qubit& q, bool reverse) {
103
1/2
✓ Branch 1 taken 814 times.
✗ Branch 2 not taken.
814 QubitPauliMap::iterator it = qpt.string.map.find(q);
104
2/2
✓ Branch 2 taken 46 times.
✓ Branch 3 taken 768 times.
2/2
✓ Decision 'true' taken 46 times.
✓ Decision 'false' taken 768 times.
814 if (it == qpt.string.map.end()) {
105 46 return;
106 }
107
1/2
✓ Branch 2 taken 768 times.
✗ Branch 3 not taken.
768 std::pair<Pauli, bool> conj = conjugate_Pauli(op, it->second, reverse);
108 768 it->second = conj.first;
109
2/2
✓ Branch 0 taken 180 times.
✓ Branch 1 taken 588 times.
2/2
✓ Decision 'true' taken 180 times.
✓ Decision 'false' taken 588 times.
768 if (conj.second) {
110 180 qpt.coeff *= -1;
111 }
112 }
113
114 965 void conjugate_PauliTensor(
115 QubitPauliTensor& qpt, OpType op, const Qubit& q0, const Qubit& q1) {
116 static const std::map<std::pair<Pauli, Pauli>, std::tuple<Pauli, Pauli, bool>>
117 cx_conj_lut{
118 {{Pauli::I, Pauli::I}, {Pauli::I, Pauli::I, false}},
119 {{Pauli::I, Pauli::X}, {Pauli::I, Pauli::X, false}},
120 {{Pauli::I, Pauli::Y}, {Pauli::Z, Pauli::Y, false}},
121 {{Pauli::I, Pauli::Z}, {Pauli::Z, Pauli::Z, false}},
122 {{Pauli::X, Pauli::I}, {Pauli::X, Pauli::X, false}},
123 {{Pauli::X, Pauli::X}, {Pauli::X, Pauli::I, false}},
124 {{Pauli::X, Pauli::Y}, {Pauli::Y, Pauli::Z, false}},
125 {{Pauli::X, Pauli::Z}, {Pauli::Y, Pauli::Y, true}},
126 {{Pauli::Y, Pauli::I}, {Pauli::Y, Pauli::X, false}},
127 {{Pauli::Y, Pauli::X}, {Pauli::Y, Pauli::I, false}},
128 {{Pauli::Y, Pauli::Y}, {Pauli::X, Pauli::Z, true}},
129 {{Pauli::Y, Pauli::Z}, {Pauli::X, Pauli::Y, false}},
130 {{Pauli::Z, Pauli::I}, {Pauli::Z, Pauli::I, false}},
131 {{Pauli::Z, Pauli::X}, {Pauli::Z, Pauli::X, false}},
132 {{Pauli::Z, Pauli::Y}, {Pauli::I, Pauli::Y, false}},
133 {{Pauli::Z, Pauli::Z}, {Pauli::I, Pauli::Z, false}},
134
4/8
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 964 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
965 };
135
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 965 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 965 times.
965 if (op != OpType::CX) {
136
0/1
? Decision couldn't be analyzed.
throw BadOpType("Conjugations of Pauli strings only defined for CXs", op);
137 }
138
1/2
✓ Branch 1 taken 965 times.
✗ Branch 2 not taken.
965 QubitPauliMap::iterator it0 = qpt.string.map.find(q0);
139
1/2
✓ Branch 1 taken 965 times.
✗ Branch 2 not taken.
965 QubitPauliMap::iterator it1 = qpt.string.map.find(q1);
140 Pauli p0, p1;
141
2/2
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 960 times.
2/2
✓ Decision 'true' taken 5 times.
✓ Decision 'false' taken 960 times.
965 if (it0 == qpt.string.map.end()) {
142 5 p0 = Pauli::I;
143 } else {
144 960 p0 = it0->second;
145 }
146
2/2
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 957 times.
2/2
✓ Decision 'true' taken 8 times.
✓ Decision 'false' taken 957 times.
965 if (it1 == qpt.string.map.end()) {
147 8 p1 = Pauli::I;
148 } else {
149 957 p1 = it1->second;
150 }
151
1/2
✓ Branch 2 taken 965 times.
✗ Branch 3 not taken.
965 std::tuple<Pauli, Pauli, bool> conj = cx_conj_lut.at({p0, p1});
152
1/2
✓ Branch 2 taken 965 times.
✗ Branch 3 not taken.
965 qpt.string.map[q0] = std::get<0>(conj);
153
1/2
✓ Branch 2 taken 965 times.
✗ Branch 3 not taken.
965 qpt.string.map[q1] = std::get<1>(conj);
154
2/2
✓ Branch 1 taken 133 times.
✓ Branch 2 taken 832 times.
2/2
✓ Decision 'true' taken 133 times.
✓ Decision 'false' taken 832 times.
965 if (std::get<2>(conj)) {
155 133 qpt.coeff *= -1;
156 }
157 965 }
158
159 21 void conjugate_PauliTensor(
160 QubitPauliTensor& qpt, OpType op, const Qubit& q0, const Qubit& q1,
161 const Qubit& q2) {
162 /* XXPhase3 gates used for conjugations always implicitly use angle π/2
163 * i.e. XXPhase3(1/2). Note that up to phase the 3-qb gate is self-inverse:
164 * XXPhase3(1/2) == exp(π/2i) XXPhase3(-1/2).
165 *
166 * We conjugate XXPhase3(1/2) by conjugating its CX-equivalent circuit
167 * __________
168 * --| |-- ------- X--H--C--H--X--
169 * | XXPhase3 | | |
170 * --| (1/2) |-- = exp(-3π/4i) --H--C--C--H--+-----X--
171 * | | | |
172 * --|__________|-- -----X--------X-----X--
173 *
174 * We can safely ignore phase differences as for each conjugated XXPhase3,
175 * we insert XXPhase3(1/2) and its dagger XXPhase3(-1/2), so that the phases
176 * always cancel out.
177 */
178
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 21 times.
21 if (op != OpType::XXPhase3) {
179 throw BadOpType(
180 "3qb-Conjugations of Pauli strings only defined for XXPhase3", op);
181 }
182 Conjugations equiv = {{OpType::H, {q1}}, {OpType::CX, {q1, q2}},
183 {OpType::CX, {q1, q0}}, {OpType::H, {q0}},
184 {OpType::H, {q1}}, {OpType::CX, {q0, q2}},
185 {OpType::H, {q0}}, {OpType::X, {q0}},
186
21/42
✓ Branch 3 taken 21 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 21 times.
✗ Branch 7 not taken.
✓ Branch 12 taken 21 times.
✗ Branch 13 not taken.
✓ Branch 15 taken 21 times.
✗ Branch 16 not taken.
✓ Branch 21 taken 21 times.
✗ Branch 22 not taken.
✓ Branch 24 taken 21 times.
✗ Branch 25 not taken.
✓ Branch 29 taken 21 times.
✗ Branch 30 not taken.
✓ Branch 32 taken 21 times.
✗ Branch 33 not taken.
✓ Branch 37 taken 21 times.
✗ Branch 38 not taken.
✓ Branch 40 taken 21 times.
✗ Branch 41 not taken.
✓ Branch 46 taken 21 times.
✗ Branch 47 not taken.
✓ Branch 49 taken 21 times.
✗ Branch 50 not taken.
✓ Branch 54 taken 21 times.
✗ Branch 55 not taken.
✓ Branch 57 taken 21 times.
✗ Branch 58 not taken.
✓ Branch 62 taken 21 times.
✗ Branch 63 not taken.
✓ Branch 65 taken 21 times.
✗ Branch 66 not taken.
✓ Branch 70 taken 21 times.
✗ Branch 71 not taken.
✓ Branch 73 taken 21 times.
✗ Branch 74 not taken.
✓ Branch 78 taken 21 times.
✗ Branch 79 not taken.
✓ Branch 81 taken 21 times.
✗ Branch 82 not taken.
✓ Branch 85 taken 21 times.
✗ Branch 86 not taken.
735 {OpType::X, {q1}}, {OpType::X, {q2}}};
187
188
3/4
✓ Branch 4 taken 210 times.
✗ Branch 5 not taken.
✓ Branch 10 taken 210 times.
✓ Branch 11 taken 21 times.
0/1
? Decision couldn't be analyzed.
231 for (auto [op, qbs] : equiv) {
189
2/2
✓ Branch 1 taken 147 times.
✓ Branch 2 taken 63 times.
2/2
✓ Decision 'true' taken 147 times.
✓ Decision 'false' taken 63 times.
210 if (qbs.size() == 1) {
190
1/2
✓ Branch 2 taken 147 times.
✗ Branch 3 not taken.
147 conjugate_PauliTensor(qpt, op, qbs[0]);
191 } else {
192 TKET_ASSERT(qbs.size() == 2);
193
1/2
✓ Branch 3 taken 63 times.
✗ Branch 4 not taken.
63 conjugate_PauliTensor(qpt, op, qbs[0], qbs[1]);
194 }
195 210 }
196 21 }
197
198 } // namespace tket
199