GCC Code Coverage Report


Directory: ./
File: Clifford/UnitaryTableau.cpp
Date: 2022-10-15 05:10:18
Warnings: 7 unchecked decisions!
Exec Total Coverage
Lines: 396 433 91.5%
Functions: 27 27 100.0%
Branches: 427 813 52.5%
Decisions: 88 129 68.2%

Line Branch Decision Exec Source
1 // Copyleft 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 "UnitaryTableau.hpp"
16
17 #include <stdexcept>
18
19 #include "OpType/OpTypeInfo.hpp"
20 #include "tkassert/Assert.hpp"
21
22 namespace tket {
23
24
2/4
✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 30 times.
✗ Branch 8 not taken.
30 UnitaryTableau::UnitaryTableau(unsigned n) : tab_({}) {
25
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 MatrixXb xmat(2 * n, n);
26
4/8
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 30 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 30 times.
✗ Branch 11 not taken.
30 xmat << MatrixXb::Identity(n, n), MatrixXb::Zero(n, n);
27
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 MatrixXb zmat(2 * n, n);
28
4/8
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 30 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 30 times.
✗ Branch 11 not taken.
30 zmat << MatrixXb::Zero(n, n), MatrixXb::Identity(n, n);
29
3/6
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 30 times.
✗ Branch 8 not taken.
30 tab_ = SymplecticTableau(xmat, zmat, VectorXb::Zero(2 * n));
30
2/4
✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 30 times.
✗ Branch 6 not taken.
30 qubits_ = boost::bimap<Qubit, unsigned>();
31
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 30 times.
2/2
✓ Decision 'true' taken 78 times.
✓ Decision 'false' taken 30 times.
108 for (unsigned i = 0; i < n; ++i) {
32
3/6
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 78 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 78 times.
✗ Branch 8 not taken.
78 qubits_.insert({Qubit(i), i});
33 }
34 30 }
35
36 13 UnitaryTableau::UnitaryTableau(const qubit_vector_t& qbs)
37 13 : UnitaryTableau(qbs.size()) {
38
2/4
✓ Branch 2 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 13 times.
✗ Branch 6 not taken.
13 qubits_ = boost::bimap<Qubit, unsigned>();
39
2/2
✓ Branch 1 taken 38 times.
✓ Branch 2 taken 13 times.
2/2
✓ Decision 'true' taken 38 times.
✓ Decision 'false' taken 13 times.
51 for (unsigned i = 0; i < qbs.size(); ++i) {
40
2/4
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 38 times.
✗ Branch 6 not taken.
38 qubits_.insert({qbs[i], i});
41 }
42 13 }
43
44 3 UnitaryTableau::UnitaryTableau(
45 const MatrixXb& xx, const MatrixXb& xz, const VectorXb& xph,
46 3 const MatrixXb& zx, const MatrixXb& zz, const VectorXb& zph)
47
2/4
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
3 : tab_({}) {
48 3 unsigned n_qubits = xx.rows();
49
1/2
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 6 times.
6 if ((xx.cols() != n_qubits) || (xz.rows() != n_qubits) ||
50
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 (xz.cols() != n_qubits) || (xph.size() != n_qubits) ||
51
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 (zx.rows() != n_qubits) || (zx.cols() != n_qubits) ||
52
4/8
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 3 times.
9 (zz.rows() != n_qubits) || (zz.cols() != n_qubits) ||
53
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 (zph.size() != n_qubits))
54 throw std::invalid_argument(
55 "Unitary tableau requires equally-sized square matrices and vectors");
56
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 MatrixXb xmat(2 * n_qubits, n_qubits);
57
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 xmat << xx, zx;
58
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 MatrixXb zmat(2 * n_qubits, n_qubits);
59
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 zmat << xz, zz;
60
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 VectorXb phase(2 * n_qubits);
61
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 phase << xph, zph;
62
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 tab_ = SymplecticTableau(xmat, zmat, phase);
63
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 MatrixXb expected_anticommutes(2 * n_qubits, 2 * n_qubits);
64
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 expected_anticommutes << MatrixXb::Zero(n_qubits, n_qubits),
65
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 MatrixXb::Identity(n_qubits, n_qubits),
66
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 MatrixXb::Identity(n_qubits, n_qubits),
67
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 MatrixXb::Zero(n_qubits, n_qubits);
68
3/6
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 3 times.
3 if (tab_.anticommuting_rows() != expected_anticommutes)
69 throw std::invalid_argument(
70 "Rows of tableau do not (anti-)commute as expected for UnitaryTableau");
71
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 3 times.
3 if (tab_.rank() != 2 * n_qubits)
72 throw std::invalid_argument(
73 "Rows of UnitaryTableau are not linearly independent");
74
2/4
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
3 qubits_ = boost::bimap<Qubit, unsigned>();
75
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 3 times.
2/2
✓ Decision 'true' taken 9 times.
✓ Decision 'false' taken 3 times.
12 for (unsigned i = 0; i < n_qubits; ++i) {
76
3/6
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 9 times.
✗ Branch 8 not taken.
9 qubits_.insert({Qubit(i), i});
77 }
78 3 }
79
80 42 QubitPauliTensor UnitaryTableau::get_xrow(const Qubit& qb) const {
81
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 unsigned uq = qubits_.left.at(qb);
82
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 PauliStabiliser stab = tab_.get_pauli(uq);
83 42 std::list<Qubit> qbs;
84
3/4
✓ Branch 1 taken 168 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 126 times.
✓ Branch 4 taken 42 times.
0/1
? Decision couldn't be analyzed.
168 for (unsigned i = 0; i < qubits_.size(); ++i) {
85
2/4
✓ Branch 1 taken 126 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 126 times.
✗ Branch 5 not taken.
126 qbs.push_back(qubits_.right.at(i));
86 }
87
1/2
✓ Branch 4 taken 42 times.
✗ Branch 5 not taken.
42 std::list<Pauli> string = {stab.string.begin(), stab.string.end()};
88 42 Complex coeff = 1.;
89
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 26 times.
0/1
? Decision couldn't be analyzed.
42 if (!stab.coeff) coeff *= -1.;
90
2/4
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 42 times.
✗ Branch 5 not taken.
126 return QubitPauliTensor(QubitPauliString(qbs, string), coeff);
91 42 }
92
93 34 QubitPauliTensor UnitaryTableau::get_zrow(const Qubit& qb) const {
94
1/2
✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
34 unsigned uqb = qubits_.left.at(qb);
95
2/4
✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 34 times.
✗ Branch 5 not taken.
34 PauliStabiliser stab = tab_.get_pauli(uqb + qubits_.size());
96 34 std::list<Qubit> qbs;
97
3/4
✓ Branch 1 taken 136 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 102 times.
✓ Branch 4 taken 34 times.
0/1
? Decision couldn't be analyzed.
136 for (unsigned i = 0; i < qubits_.size(); ++i) {
98
2/4
✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 102 times.
✗ Branch 5 not taken.
102 qbs.push_back(qubits_.right.at(i));
99 }
100
1/2
✓ Branch 4 taken 34 times.
✗ Branch 5 not taken.
34 std::list<Pauli> string = {stab.string.begin(), stab.string.end()};
101 34 Complex coeff = 1.;
102
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 23 times.
0/1
? Decision couldn't be analyzed.
34 if (!stab.coeff) coeff *= -1.;
103
2/4
✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 34 times.
✗ Branch 5 not taken.
102 return QubitPauliTensor(QubitPauliString(qbs, string), coeff);
104 34 }
105
106 18 QubitPauliTensor UnitaryTableau::get_row_product(
107 const QubitPauliTensor& qpt) const {
108 18 QubitPauliTensor result(qpt.coeff);
109
2/2
✓ Branch 5 taken 54 times.
✓ Branch 6 taken 18 times.
2/2
✓ Decision 'true' taken 54 times.
✓ Decision 'false' taken 18 times.
72 for (const std::pair<const Qubit, Pauli>& p : qpt.string.map) {
110
1/2
✓ Branch 1 taken 54 times.
✗ Branch 2 not taken.
54 auto qks_it = qubits_.left.find(p.first);
111
3/6
✓ Branch 1 taken 54 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 54 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 54 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 54 times.
54 if (qks_it == qubits_.left.end()) {
112 // Acts as identity on p.first
113 result = result * QubitPauliTensor(p.first, p.second);
114 } else {
115
4/5
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 21 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 13 times.
✗ Branch 4 not taken.
54 switch (p.second) {
116
1/1
✓ Decision 'true' taken 16 times.
16 case Pauli::I: {
117 16 break;
118 }
119
1/1
✓ Decision 'true' taken 21 times.
21 case Pauli::X: {
120
2/4
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 21 times.
✗ Branch 5 not taken.
21 result = result * get_xrow(p.first);
121 21 break;
122 }
123
0/1
✗ Decision 'true' not taken.
4 case Pauli::Y: {
124 // Y = iXZ
125
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 result = result * get_xrow(p.first);
126
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 result = result * get_zrow(p.first);
127 4 result.coeff *= i_;
128 4 break;
129 }
130
1/1
✓ Decision 'true' taken 13 times.
13 case Pauli::Z: {
131
2/4
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 13 times.
✗ Branch 5 not taken.
13 result = result * get_zrow(p.first);
132 13 break;
133 }
134 }
135 }
136 }
137 18 return result;
138 }
139
140 39 std::set<Qubit> UnitaryTableau::get_qubits() const {
141 39 std::set<Qubit> result;
142
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
1/2
✓ Decision 'true' taken 39 times.
✗ Decision 'false' not taken.
78 for (boost::bimap<Qubit, unsigned>::const_iterator iter = qubits_.begin(),
143
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 iend = qubits_.end();
144
4/6
✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 156 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 117 times.
✓ Branch 7 taken 39 times.
156 iter != iend; ++iter) {
145
2/4
✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 117 times.
✗ Branch 5 not taken.
117 result.insert(iter->left);
146 }
147 39 return result;
148 }
149
150 21 void UnitaryTableau::apply_S_at_front(const Qubit& qb) {
151 21 unsigned uqb = qubits_.left.at(qb);
152 21 tab_.row_mult(uqb + qubits_.size(), uqb, i_);
153 21 }
154
155 1 void UnitaryTableau::apply_S_at_end(const Qubit& qb) {
156 1 unsigned uqb = qubits_.left.at(qb);
157 1 tab_.apply_S(uqb);
158 1 }
159
160 18 void UnitaryTableau::apply_V_at_front(const Qubit& qb) {
161 18 unsigned uqb = qubits_.left.at(qb);
162 18 tab_.row_mult(uqb, uqb + qubits_.size(), i_);
163 18 }
164
165 1 void UnitaryTableau::apply_V_at_end(const Qubit& qb) {
166 1 unsigned uqb = qubits_.left.at(qb);
167 1 tab_.apply_V(uqb);
168 1 }
169
170 15 void UnitaryTableau::apply_CX_at_front(
171 const Qubit& control, const Qubit& target) {
172 15 unsigned uc = qubits_.left.at(control);
173 15 unsigned ut = qubits_.left.at(target);
174
1/2
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
15 tab_.row_mult(ut, uc, 1.);
175
3/6
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 15 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 15 times.
✗ Branch 9 not taken.
15 tab_.row_mult(uc + qubits_.size(), ut + qubits_.size());
176 15 }
177
178 1 void UnitaryTableau::apply_CX_at_end(
179 const Qubit& control, const Qubit& target) {
180 1 unsigned uc = qubits_.left.at(control);
181 1 unsigned ut = qubits_.left.at(target);
182 1 tab_.apply_CX(uc, ut);
183 1 }
184
185 24 void UnitaryTableau::apply_gate_at_front(
186 OpType type, const qubit_vector_t& qbs) {
187
12/15
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 8 times.
✓ Branch 9 taken 1 times.
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 1 times.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 1 times.
24 switch (type) {
188
1/1
✓ Decision 'true' taken 2 times.
2 case OpType::Z: {
189 2 apply_S_at_front(qbs.at(0));
190 2 apply_S_at_front(qbs.at(0));
191 2 break;
192 }
193
1/1
✓ Decision 'true' taken 2 times.
2 case OpType::X: {
194 2 apply_V_at_front(qbs.at(0));
195 2 apply_V_at_front(qbs.at(0));
196 2 break;
197 }
198
1/1
✓ Decision 'true' taken 2 times.
2 case OpType::Y: {
199 2 apply_S_at_front(qbs.at(0));
200 2 apply_S_at_front(qbs.at(0));
201 2 apply_V_at_front(qbs.at(0));
202 2 apply_V_at_front(qbs.at(0));
203 2 break;
204 }
205
1/1
✓ Decision 'true' taken 2 times.
2 case OpType::S: {
206 2 apply_S_at_front(qbs.at(0));
207 2 break;
208 }
209
0/1
✗ Decision 'true' not taken.
case OpType::Sdg: {
210 apply_S_at_front(qbs.at(0));
211 apply_S_at_front(qbs.at(0));
212 apply_S_at_front(qbs.at(0));
213 break;
214 }
215
0/1
✗ Decision 'true' not taken.
case OpType::V: {
216 apply_V_at_front(qbs.at(0));
217 break;
218 }
219
1/1
✓ Decision 'true' taken 2 times.
2 case OpType::Vdg: {
220 2 apply_V_at_front(qbs.at(0));
221 2 apply_V_at_front(qbs.at(0));
222 2 apply_V_at_front(qbs.at(0));
223 2 break;
224 }
225
1/1
✓ Decision 'true' taken 1 times.
1 case OpType::H: {
226 1 apply_S_at_front(qbs.at(0));
227 1 apply_V_at_front(qbs.at(0));
228 1 apply_S_at_front(qbs.at(0));
229 1 break;
230 }
231
1/1
✓ Decision 'true' taken 8 times.
8 case OpType::CX: {
232 8 apply_CX_at_front(qbs.at(0), qbs.at(1));
233 8 break;
234 }
235
1/1
✓ Decision 'true' taken 1 times.
1 case OpType::CY: {
236 1 apply_S_at_front(qbs.at(1));
237 1 apply_CX_at_front(qbs.at(0), qbs.at(1));
238 1 apply_S_at_front(qbs.at(1));
239 1 apply_S_at_front(qbs.at(1));
240 1 apply_S_at_front(qbs.at(1));
241 1 break;
242 }
243
1/1
✓ Decision 'true' taken 1 times.
1 case OpType::CZ: {
244 1 apply_S_at_front(qbs.at(1));
245 1 apply_V_at_front(qbs.at(1));
246 1 apply_S_at_front(qbs.at(1));
247 1 apply_CX_at_front(qbs.at(0), qbs.at(1));
248 1 apply_S_at_front(qbs.at(1));
249 1 apply_V_at_front(qbs.at(1));
250 1 apply_S_at_front(qbs.at(1));
251 1 break;
252 }
253
1/1
✓ Decision 'true' taken 1 times.
1 case OpType::SWAP: {
254 1 apply_CX_at_front(qbs.at(0), qbs.at(1));
255 1 apply_CX_at_front(qbs.at(1), qbs.at(0));
256 1 apply_CX_at_front(qbs.at(0), qbs.at(1));
257 1 break;
258 }
259
1/1
✓ Decision 'true' taken 1 times.
1 case OpType::BRIDGE: {
260 1 apply_CX_at_front(qbs.at(0), qbs.at(2));
261 1 break;
262 }
263
0/1
✗ Decision 'true' not taken.
case OpType::noop: {
264 break;
265 }
266
1/1
✓ Decision 'true' taken 1 times.
1 default: {
267
2/4
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
1 throw BadOpType(
268 2 "Cannot be applied to a UnitaryTableau: not a Clifford gate", type);
269 }
270 }
271 23 }
272
273 127 void UnitaryTableau::apply_gate_at_end(OpType type, const qubit_vector_t& qbs) {
274 127 std::vector<unsigned> uqbs;
275
2/2
✓ Branch 5 taken 197 times.
✓ Branch 6 taken 127 times.
2/2
✓ Decision 'true' taken 197 times.
✓ Decision 'false' taken 127 times.
324 for (const Qubit& q : qbs) {
276
2/4
✓ Branch 1 taken 197 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 197 times.
✗ Branch 5 not taken.
197 uqbs.push_back(qubits_.left.at(q));
277 }
278
2/2
✓ Branch 1 taken 125 times.
✓ Branch 2 taken 2 times.
127 tab_.apply_gate(type, uqbs);
279 127 }
280
281 2 void UnitaryTableau::apply_pauli_at_front(
282 const QubitPauliTensor& pauli, unsigned half_pis) {
283 2 half_pis = half_pis % 4;
284
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2/2
✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 1 times.
3 if (half_pis == 0) return; // Identity
285
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
0/1
? Decision couldn't be analyzed.
2 if (half_pis == 2) { // Degenerates to product of PI rotations
286
2/2
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 1 times.
2/2
✓ Decision 'true' taken 3 times.
✓ Decision 'false' taken 1 times.
4 for (const std::pair<const Qubit, Pauli>& term : pauli.string.map) {
287
3/5
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
3 switch (term.second) {
288
0/1
✗ Decision 'true' not taken.
case Pauli::I: {
289 break;
290 }
291
1/1
✓ Decision 'true' taken 2 times.
1 case Pauli::X: {
292
4/8
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 1 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
2 apply_gate_at_front(OpType::X, {term.first});
293 1 break;
294 }
295
1/1
✓ Decision 'true' taken 2 times.
1 case Pauli::Y: {
296
4/8
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 1 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
2 apply_gate_at_front(OpType::Y, {term.first});
297 1 break;
298 }
299
1/1
✓ Decision 'true' taken 2 times.
1 case Pauli::Z: {
300
4/8
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 1 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
2 apply_gate_at_front(OpType::Z, {term.first});
301 1 break;
302 }
303 }
304 }
305 1 return;
306 }
307
308 // From here, half_pis == 1 or 3
309 // They act the same except for a phase flip on the product term
310
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
2 MatrixXb product_x = MatrixXb::Zero(1, qubits_.size());
311
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
2 MatrixXb product_z = MatrixXb::Zero(1, qubits_.size());
312
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 MatrixXb::RowXpr px = product_x.row(0);
313
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 MatrixXb::RowXpr pz = product_z.row(0);
314
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 1 times.
1 if (pauli.coeff != 1. && pauli.coeff != -1.)
315 throw std::domain_error(
316 "Can only apply Pauli gadgets with real unit coefficients to "
317 "UnitaryTableaux");
318 1 bool phase = (pauli.coeff == -1.) ^ (half_pis == 1);
319
320 // Collect the product term
321
2/2
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 1 times.
2/2
✓ Decision 'true' taken 3 times.
✓ Decision 'false' taken 1 times.
4 for (const std::pair<const Qubit, Pauli>& term : pauli.string.map) {
322
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 unsigned uqb = qubits_.left.at(term.first);
323
3/5
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
3 switch (term.second) {
324
0/1
✗ Decision 'true' not taken.
case Pauli::I: {
325 break;
326 }
327
1/1
✓ Decision 'true' taken 2 times.
1 case Pauli::X: {
328
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 tab_.row_mult(
329
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
1 tab_.xmat_.row(uqb), tab_.zmat_.row(uqb), tab_.phase_(uqb), px, pz,
330 phase, 1., px, pz, phase);
331 1 break;
332 }
333
1/1
✓ Decision 'true' taken 2 times.
1 case Pauli::Y: {
334
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 tab_.row_mult(
335
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
1 tab_.xmat_.row(uqb), tab_.zmat_.row(uqb), tab_.phase_(uqb), px, pz,
336 phase, 1., px, pz, phase);
337
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 tab_.row_mult(
338
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 tab_.xmat_.row(uqb + qubits_.size()),
339
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 tab_.zmat_.row(uqb + qubits_.size()),
340
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
1 tab_.phase_(uqb + qubits_.size()), px, pz, phase, 1., px, pz,
341 phase);
342 1 break;
343 }
344
1/1
✓ Decision 'true' taken 2 times.
1 case Pauli::Z: {
345
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 tab_.row_mult(
346
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 tab_.xmat_.row(uqb + qubits_.size()),
347
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 tab_.zmat_.row(uqb + qubits_.size()),
348
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
1 tab_.phase_(uqb + qubits_.size()), px, pz, phase, 1., px, pz,
349 phase);
350 1 break;
351 }
352 }
353 }
354
355 // Apply the product term on the anti-commuting rows
356
2/2
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 1 times.
2/2
✓ Decision 'true' taken 3 times.
✓ Decision 'false' taken 1 times.
4 for (const std::pair<const Qubit, Pauli>& term : pauli.string.map) {
357
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 unsigned uqb = qubits_.left.at(term.first);
358
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 MatrixXb::RowXpr xx = tab_.xmat_.row(uqb);
359
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 MatrixXb::RowXpr xz = tab_.zmat_.row(uqb);
360
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 MatrixXb::RowXpr zx = tab_.xmat_.row(uqb + qubits_.size());
361
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 MatrixXb::RowXpr zz = tab_.zmat_.row(uqb + qubits_.size());
362
3/5
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
3 switch (term.second) {
363
0/1
✗ Decision 'true' not taken.
case Pauli::I: {
364 break;
365 }
366
1/1
✓ Decision 'true' taken 3 times.
1 case Pauli::X: {
367 3 tab_.row_mult(
368
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
1 px, pz, phase, zx, zz, tab_.phase_(uqb + qubits_.size()), -i_, zx,
369
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 zz, tab_.phase_(uqb + qubits_.size()));
370 1 break;
371 }
372
1/1
✓ Decision 'true' taken 3 times.
1 case Pauli::Y: {
373 3 tab_.row_mult(
374
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
1 px, pz, phase, zx, zz, tab_.phase_(uqb + qubits_.size()), -i_, zx,
375
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 zz, tab_.phase_(uqb + qubits_.size()));
376 3 tab_.row_mult(
377
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 px, pz, phase, xx, xz, tab_.phase_(uqb), -i_, xx, xz,
378
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 tab_.phase_(uqb));
379 1 break;
380 }
381
1/1
✓ Decision 'true' taken 3 times.
1 case Pauli::Z: {
382 3 tab_.row_mult(
383
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 px, pz, phase, xx, xz, tab_.phase_(uqb), -i_, xx, xz,
384
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 tab_.phase_(uqb));
385 1 break;
386 }
387 }
388 }
389 }
390
391 1 void UnitaryTableau::apply_pauli_at_end(
392 const QubitPauliTensor& pauli, unsigned half_pis) {
393
2/4
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
1 std::vector<Pauli> string(qubits_.size(), Pauli::I);
394
2/2
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 1 times.
2/2
✓ Decision 'true' taken 3 times.
✓ Decision 'false' taken 1 times.
4 for (const std::pair<const Qubit, Pauli>& pair : pauli.string.map) {
395
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 unsigned uqb = qubits_.left.at(pair.first);
396
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 string.at(uqb) = pair.second;
397 }
398
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 1 times.
1 if (pauli.coeff != 1. && pauli.coeff != -1.)
399 throw std::domain_error(
400 "Can only apply Pauli gadgets with real unit coefficients to "
401 "UnitaryTableaux");
402
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
1 tab_.apply_pauli_gadget({string, pauli.coeff == 1.}, half_pis);
403 1 }
404
405 1 UnitaryTableau UnitaryTableau::compose(
406 const UnitaryTableau& first, const UnitaryTableau& second) {
407
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 std::set<Qubit> qbs = first.get_qubits();
408
3/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 8 taken 3 times.
✓ Branch 9 taken 1 times.
0/1
? Decision couldn't be analyzed.
4 for (const Qubit& q : second.get_qubits()) {
409
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 qbs.insert(q);
410 1 }
411
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 UnitaryTableau result = UnitaryTableau({});
412 1 const unsigned nqb = qbs.size();
413
414 1 std::vector<QubitPauliTensor> rows;
415
416 1 unsigned qir = 0;
417
2/2
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 1 times.
2/2
✓ Decision 'true' taken 3 times.
✓ Decision 'false' taken 1 times.
4 for (const Qubit& qi : qbs) {
418
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 auto qif_it = first.qubits_.left.find(qi);
419
3/6
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 3 times.
3 if (qif_it == first.qubits_.left.end()) {
420 // First acts as identity on qi, so just take effect of second
421 rows.push_back(second.get_xrow(qi));
422 } else {
423 // Sum rows of second according to entries of first
424
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 QubitPauliTensor fxrow = first.get_xrow(qi);
425
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 QubitPauliTensor rxrow = second.get_row_product(fxrow);
426
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 rows.push_back(rxrow);
427 3 }
428
429
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 result.qubits_.insert({qi, qir});
430 3 ++qir;
431 }
432
433 // Do the same for the Z rows
434
2/2
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 1 times.
2/2
✓ Decision 'true' taken 3 times.
✓ Decision 'false' taken 1 times.
4 for (const Qubit& qi : qbs) {
435
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 auto qif_it = first.qubits_.left.find(qi);
436
3/6
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 3 times.
3 if (qif_it == first.qubits_.left.end()) {
437 // First acts as identity on qi, so just take effect of second
438 rows.push_back(second.get_zrow(qi));
439 } else {
440 // Sum rows of second according to entries of first
441
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 QubitPauliTensor fzrow = first.get_zrow(qi);
442
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 QubitPauliTensor rzrow = second.get_row_product(fzrow);
443
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 rows.push_back(rzrow);
444 3 }
445 }
446
447 // Combine row lists and convert to PauliStabilisers
448 1 PauliStabiliserList all_rows;
449
2/2
✓ Branch 5 taken 6 times.
✓ Branch 6 taken 1 times.
2/2
✓ Decision 'true' taken 6 times.
✓ Decision 'false' taken 1 times.
7 for (const QubitPauliTensor& row : rows) {
450 TKET_ASSERT(row.coeff == 1. || row.coeff == -1.);
451
1/2
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 std::vector<Pauli> ps(nqb, Pauli::I);
452
2/2
✓ Branch 4 taken 14 times.
✓ Branch 5 taken 6 times.
2/2
✓ Decision 'true' taken 14 times.
✓ Decision 'false' taken 6 times.
20 for (const std::pair<const Qubit, Pauli>& p : row.string.map) {
453
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 unsigned q = result.qubits_.left.at(p.first);
454 14 ps[q] = p.second;
455 }
456
3/6
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 6 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 6 times.
✗ Branch 9 not taken.
6 all_rows.push_back(PauliStabiliser(ps, row.coeff == 1.));
457 6 }
458
459
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 result.tab_ = SymplecticTableau(all_rows);
460
461 2 return result;
462 1 }
463
464 static const std::map<
465 std::pair<BoolPauli, BoolPauli>, std::pair<BoolPauli, BoolPauli>>&
466 18 invert_cell_map() {
467 1 static const auto inv_map = []() {
468 1 const BoolPauli I = {false, false};
469 1 const BoolPauli X = {true, false};
470 1 const BoolPauli Y = {true, true};
471 1 const BoolPauli Z = {false, true};
472 return std::map<
473 std::pair<BoolPauli, BoolPauli>, std::pair<BoolPauli, BoolPauli>>{
474 {{I, I}, {I, I}}, {{I, X}, {I, X}}, {{I, Y}, {X, X}}, {{I, Z}, {X, I}},
475 {{X, I}, {I, Z}}, {{X, X}, {I, Y}}, {{X, Y}, {X, Y}}, {{X, Z}, {X, Z}},
476 {{Y, I}, {Z, Z}}, {{Y, X}, {Z, Y}}, {{Y, Y}, {Y, Y}}, {{Y, Z}, {Y, Z}},
477 {{Z, I}, {Z, I}}, {{Z, X}, {Z, X}}, {{Z, Y}, {Y, X}}, {{Z, Z}, {Y, I}},
478
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 };
479
4/8
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 17 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
18 }();
480 18 return inv_map;
481 }
482
483 2 UnitaryTableau UnitaryTableau::dagger() const {
484 // This method is following Craig Gidney's tableau inversion method
485 // https://algassert.com/post/2002
486
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 unsigned nqb = qubits_.size();
487
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 MatrixXb dxx = MatrixXb::Zero(nqb, nqb);
488
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 MatrixXb dxz = MatrixXb::Zero(nqb, nqb);
489
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 VectorXb dxph = VectorXb::Zero(nqb);
490
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 MatrixXb dzx = MatrixXb::Zero(nqb, nqb);
491
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 MatrixXb dzz = MatrixXb::Zero(nqb, nqb);
492
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 VectorXb dzph = VectorXb::Zero(nqb);
493
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
2/2
✓ Decision 'true' taken 6 times.
✓ Decision 'false' taken 2 times.
8 for (unsigned i = 0; i < nqb; ++i) {
494
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 6 times.
2/2
✓ Decision 'true' taken 18 times.
✓ Decision 'false' taken 6 times.
24 for (unsigned j = 0; j < nqb; ++j) {
495 // Take effect of some input on some output and invert
496
2/4
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
36 auto inv_cell = invert_cell_map().at(
497
2/4
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 {BoolPauli{tab_.xmat_(i, j), tab_.zmat_(i, j)},
498
2/4
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 BoolPauli{tab_.xmat_(i + nqb, j), tab_.zmat_(i + nqb, j)}});
499 // Transpose tableau and fill in cell
500
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 dxx(j, i) = inv_cell.first.x;
501
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 dxz(j, i) = inv_cell.first.z;
502
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 dzx(j, i) = inv_cell.second.x;
503
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 dzz(j, i) = inv_cell.second.z;
504 }
505 }
506
507
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 UnitaryTableau dag(dxx, dxz, dxph, dzx, dzz, dzph);
508
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 dag.qubits_ = qubits_;
509
510 // Correct phases
511
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
2/2
✓ Decision 'true' taken 6 times.
✓ Decision 'false' taken 2 times.
8 for (unsigned i = 0; i < nqb; ++i) {
512
2/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
6 QubitPauliTensor xr = dag.get_xrow(qubits_.right.at(i));
513
2/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 6 times.
✗ Branch 6 not taken.
6 dag.tab_.phase_(i) = get_row_product(xr).coeff == -1.;
514
2/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
6 QubitPauliTensor zr = dag.get_zrow(qubits_.right.at(i));
515
2/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 6 times.
✗ Branch 6 not taken.
6 dag.tab_.phase_(i + nqb) = get_row_product(zr).coeff == -1.;
516 6 }
517
518 4 return dag;
519 2 }
520
521 1 UnitaryTableau UnitaryTableau::transpose() const {
522
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 return dagger().conjugate();
523 }
524
525 1 UnitaryTableau UnitaryTableau::conjugate() const {
526 1 UnitaryTableau conj(0);
527
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 conj.tab_ = tab_.conjugate();
528
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 conj.qubits_ = qubits_;
529 1 return conj;
530 }
531
532 1 std::ostream& operator<<(std::ostream& os, const UnitaryTableau& tab) {
533 1 unsigned nqs = tab.qubits_.size();
534
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
2/2
✓ Decision 'true' taken 3 times.
✓ Decision 'false' taken 1 times.
4 for (unsigned i = 0; i < nqs; ++i) {
535
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 Qubit qi = tab.qubits_.right.at(i);
536
7/14
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 3 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 3 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 3 times.
✗ Branch 20 not taken.
3 os << "X@" << qi.repr() << "\t->\t" << tab.tab_.xmat_.row(i) << " "
537
6/12
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 3 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 3 times.
✗ Branch 17 not taken.
3 << tab.tab_.zmat_.row(i) << " " << tab.tab_.phase_(i) << std::endl;
538 3 }
539 1 os << "--" << std::endl;
540
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
2/2
✓ Decision 'true' taken 3 times.
✓ Decision 'false' taken 1 times.
4 for (unsigned i = 0; i < nqs; ++i) {
541
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 Qubit qi = tab.qubits_.right.at(i);
542
7/14
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 3 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 3 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 3 times.
✗ Branch 20 not taken.
3 os << "Z@" << qi.repr() << "\t->\t" << tab.tab_.xmat_.row(i + nqs) << " "
543
5/10
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 3 times.
✗ Branch 14 not taken.
6 << tab.tab_.zmat_.row(i + nqs) << " " << tab.tab_.phase_(i + nqs)
544
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 << std::endl;
545 3 }
546 1 return os;
547 }
548
549 10 bool UnitaryTableau::operator==(const UnitaryTableau& other) const {
550
3/6
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 10 times.
✗ Branch 6 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 10 times.
1/2
✓ Decision 'true' taken 10 times.
✗ Decision 'false' not taken.
10 if (get_qubits() != other.get_qubits()) return false;
551
552 10 unsigned nq = qubits_.size();
553
554
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 10 times.
2/2
✓ Decision 'true' taken 30 times.
✓ Decision 'false' taken 10 times.
40 for (unsigned i = 0; i < nq; ++i) {
555
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 Qubit qi = qubits_.right.at(i);
556
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 unsigned oi = other.qubits_.left.at(qi);
557
2/2
✓ Branch 0 taken 90 times.
✓ Branch 1 taken 30 times.
2/2
✓ Decision 'true' taken 90 times.
✓ Decision 'false' taken 30 times.
120 for (unsigned j = 0; j < nq; ++j) {
558
1/2
✓ Branch 1 taken 90 times.
✗ Branch 2 not taken.
90 Qubit qj = qubits_.right.at(j);
559
1/2
✓ Branch 1 taken 90 times.
✗ Branch 2 not taken.
90 unsigned oj = other.qubits_.left.at(qj);
560
3/6
✓ Branch 1 taken 90 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 90 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 90 times.
1/2
✓ Decision 'true' taken 90 times.
✗ Decision 'false' not taken.
90 if (tab_.xmat_(i, j) != other.tab_.xmat_(oi, oj)) return false;
561
3/6
✓ Branch 1 taken 90 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 90 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 90 times.
1/2
✓ Decision 'true' taken 90 times.
✗ Decision 'false' not taken.
90 if (tab_.zmat_(i, j) != other.tab_.zmat_(oi, oj)) return false;
562
3/6
✓ Branch 1 taken 90 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 90 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 90 times.
1/2
✓ Decision 'true' taken 90 times.
✗ Decision 'false' not taken.
90 if (tab_.xmat_(i + nq, j) != other.tab_.xmat_(oi + nq, oj)) return false;
563
3/6
✓ Branch 1 taken 90 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 90 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 90 times.
1/2
✓ Decision 'true' taken 90 times.
✗ Decision 'false' not taken.
90 if (tab_.zmat_(i + nq, j) != other.tab_.zmat_(oi + nq, oj)) return false;
564
1/2
✓ Branch 1 taken 90 times.
✗ Branch 2 not taken.
90 }
565
3/6
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 30 times.
1/2
✓ Decision 'true' taken 30 times.
✗ Decision 'false' not taken.
30 if (tab_.phase_(i) != other.tab_.phase_(oi)) return false;
566
3/6
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 30 times.
1/2
✓ Decision 'true' taken 30 times.
✗ Decision 'false' not taken.
30 if (tab_.phase_(i + nq) != other.tab_.phase_(oi + nq)) return false;
567
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 }
568
569 10 return true;
570 }
571
572 1 void to_json(nlohmann::json& j, const UnitaryTableau& tab) {
573
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 j["tab"] = tab.tab_;
574 1 qubit_vector_t qbs;
575
3/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 1 times.
0/1
? Decision couldn't be analyzed.
4 for (unsigned i = 0; i < tab.qubits_.size(); ++i) {
576
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 qbs.push_back(tab.qubits_.right.at(i));
577 }
578
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 j["qubits"] = qbs;
579 1 }
580
581 1 void from_json(const nlohmann::json& j, UnitaryTableau& tab) {
582
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 j.at("tab").get_to(tab.tab_);
583
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 1 times.
1 if (tab.tab_.get_n_rows() != 2 * tab.tab_.get_n_qubits())
584 throw std::invalid_argument(
585 "Size of tableau does not match requirements for UnitaryTableau.");
586
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 qubit_vector_t qbs = j.at("qubits").get<qubit_vector_t>();
587 1 unsigned nqbs = qbs.size();
588
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 1 times.
1 if (nqbs != tab.tab_.get_n_qubits())
589 throw std::invalid_argument(
590 "Number of qubits in json UnitaryTableau does not match tableau size.");
591
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 MatrixXb expected_anticommutes(2 * nqbs, 2 * nqbs);
592
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 expected_anticommutes << MatrixXb::Zero(nqbs, nqbs),
593
4/8
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
1 MatrixXb::Identity(nqbs, nqbs), MatrixXb::Identity(nqbs, nqbs),
594
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 MatrixXb::Zero(nqbs, nqbs);
595
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 1 times.
1 if (tab.tab_.anticommuting_rows() != expected_anticommutes)
596 throw std::invalid_argument(
597 "Rows of tableau do not (anti-)commute as expected for UnitaryTableau");
598
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 1 times.
1 if (tab.tab_.rank() != 2 * nqbs)
599 throw std::invalid_argument(
600 "Rows of UnitaryTableau are not linearly independent");
601
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 tab.qubits_.clear();
602
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
2/2
✓ Decision 'true' taken 3 times.
✓ Decision 'false' taken 1 times.
4 for (unsigned i = 0; i < nqbs; ++i) {
603
3/6
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
3 tab.qubits_.insert({qbs.at(i), i});
604 }
605 1 }
606
607 } // namespace tket
608