GCC Code Coverage Report


Directory: ./
File: Utils/PauliStrings.cpp
Date: 2022-10-15 05:10:18
Warnings: 4 unchecked decisions!
Exec Total Coverage
Lines: 263 325 80.9%
Functions: 39 50 78.0%
Branches: 256 447 57.3%
Decisions: 103 140 73.6%

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 "PauliStrings.hpp"
16
17 #include <map>
18 #include <sstream>
19 #include <stdexcept>
20 #include <string>
21 #include <tkassert/Assert.hpp>
22 #include <utility>
23 #include <vector>
24
25 #include "Utils/Constants.hpp"
26 #include "Utils/EigenConfig.hpp"
27 #include "Utils/Json.hpp"
28
29 namespace tket {
30
31 4 static const CmplxSpMat const_2x2_matrix(
32 Complex tl, Complex tr, Complex bl, Complex br) {
33 4 CmplxSpMat m(2, 2);
34
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
2/2
✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 2 times.
4 if (tl != czero) {
35
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 m.insert(0, 0) = tl;
36 }
37
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
2/2
✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 2 times.
4 if (tr != czero) {
38
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 m.insert(0, 1) = tr;
39 }
40
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
2/2
✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 2 times.
4 if (bl != czero) {
41
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 m.insert(1, 0) = bl;
42 }
43
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
2/2
✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 2 times.
4 if (br != czero) {
44
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 m.insert(1, 1) = br;
45 }
46 4 return m;
47 }
48
49 53 static const CmplxSpMat &pauli_sparse_mat(Pauli p) {
50
4/8
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 52 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
53 static const CmplxSpMat I_mat = const_2x2_matrix(1, 0, 0, 1);
51
4/8
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 52 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
53 static const CmplxSpMat X_mat = const_2x2_matrix(0, 1, 1, 0);
52
4/8
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 52 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
53 static const CmplxSpMat Y_mat = const_2x2_matrix(0, -i_, i_, 0);
53
4/8
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 52 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
53 static const CmplxSpMat Z_mat = const_2x2_matrix(1, 0, 0, -1);
54
4/4
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 9 times.
53 switch (p) {
55
1/1
✓ Decision 'true' taken 28 times.
28 case Pauli::X:
56 28 return X_mat;
57
1/1
✓ Decision 'true' taken 4 times.
4 case Pauli::Y:
58 4 return Y_mat;
59
1/1
✓ Decision 'true' taken 12 times.
12 case Pauli::Z:
60 12 return Z_mat;
61
0/1
✗ Decision 'true' not taken.
9 default:
62 TKET_ASSERT(p == Pauli::I);
63 9 return I_mat;
64 }
65 }
66
67 class StateNotPowerTwo : public std::logic_error {
68 public:
69 StateNotPowerTwo()
70 : std::logic_error("Statevector size is not a power of two.") {}
71 };
72
73 2 unsigned get_n_qb_from_statevector(const Eigen::VectorXcd &state) {
74 // allowing room for big states
75 2 unsigned long long n = state.size();
76
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 2 times.
2 if (!(n && (!(n & (n - 1))))) {
77 throw StateNotPowerTwo();
78 }
79
80 2 unsigned count = 0;
81
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 while (n) {
82 6 n >>= 1;
83 6 ++count;
84 }
85 2 return count - 1;
86 }
87
88 CmplxSpMat QubitPauliString::to_sparse_matrix() const {
89 qubit_vector_t qubits(map.size());
90 unsigned i = 0;
91
0/2
✗ Decision 'true' not taken.
✗ Decision 'false' not taken.
for (const std::pair<const Qubit, Pauli> &pair : map) {
92 qubits[i] = pair.first;
93 ++i;
94 }
95 return to_sparse_matrix(qubits);
96 }
97
98 27 CmplxSpMat QubitPauliString::to_sparse_matrix(const unsigned n_qubits) const {
99
1/2
✓ Branch 2 taken 27 times.
✗ Branch 3 not taken.
27 qubit_vector_t qubits(n_qubits);
100
2/2
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 27 times.
2/2
✓ Decision 'true' taken 53 times.
✓ Decision 'false' taken 27 times.
80 for (unsigned i = 0; i < n_qubits; ++i) {
101
1/2
✓ Branch 1 taken 53 times.
✗ Branch 2 not taken.
53 qubits[i] = Qubit(i);
102 }
103
1/2
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
54 return to_sparse_matrix(qubits);
104 27 }
105
106 29 CmplxSpMat QubitPauliString::to_sparse_matrix(
107 const qubit_vector_t &qubits) const {
108
1/2
✓ Branch 3 taken 29 times.
✗ Branch 4 not taken.
29 std::vector<Pauli> paulis(qubits.size(), Pauli::I);
109 29 std::map<Qubit, unsigned> index_map;
110 29 unsigned index = 0;
111
2/2
✓ Branch 4 taken 58 times.
✓ Branch 5 taken 29 times.
2/2
✓ Decision 'true' taken 58 times.
✓ Decision 'false' taken 29 times.
87 for (const Qubit &q : qubits) {
112
1/2
✓ Branch 2 taken 58 times.
✗ Branch 3 not taken.
58 index_map.insert({q, index});
113 58 ++index;
114 }
115
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 28 times.
2/2
✓ Decision 'true' taken 1 times.
✓ Decision 'false' taken 28 times.
29 if (index_map.size() != qubits.size())
116
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 throw std::logic_error(
117 2 "Qubit list given to to_sparse_matrix contains repeats");
118
2/2
✓ Branch 4 taken 55 times.
✓ Branch 5 taken 27 times.
2/2
✓ Decision 'true' taken 55 times.
✓ Decision 'false' taken 27 times.
82 for (const std::pair<const Qubit, Pauli> &pair : map) {
119
1/2
✓ Branch 1 taken 55 times.
✗ Branch 2 not taken.
55 std::map<Qubit, unsigned>::iterator found = index_map.find(pair.first);
120
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 54 times.
2/2
✓ Decision 'true' taken 1 times.
✓ Decision 'false' taken 54 times.
55 if (found == index_map.end())
121
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 throw std::logic_error(
122
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 "Qubit list given to to_sparse_matrix doesn't contain " +
123
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 pair.first.repr());
124
1/2
✓ Branch 2 taken 54 times.
✗ Branch 3 not taken.
54 paulis.at(found->second) = pair.second;
125 }
126
1/2
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
27 CmplxSpMat result(1, 1);
127
1/2
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
27 result.insert(0, 0) = 1;
128
2/2
✓ Branch 5 taken 53 times.
✓ Branch 6 taken 27 times.
2/2
✓ Decision 'true' taken 53 times.
✓ Decision 'false' taken 27 times.
80 for (Pauli p : paulis) {
129
2/4
✓ Branch 1 taken 53 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 53 times.
✗ Branch 5 not taken.
53 const CmplxSpMat pauli_mat = pauli_sparse_mat(p);
130
3/6
✓ Branch 1 taken 53 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 53 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 53 times.
✗ Branch 8 not taken.
53 result = Eigen::KroneckerProductSparse(result, pauli_mat).eval();
131 53 }
132 54 return result;
133 31 }
134
135 CmplxSpMat operator_tensor(
136 const OperatorSum &total_operator, unsigned n_qubits) {
137 qubit_vector_t qubits(n_qubits);
138
0/2
✗ Decision 'true' not taken.
✗ Decision 'false' not taken.
for (unsigned i = 0; i < n_qubits; ++i) {
139 qubits[i] = Qubit(i);
140 }
141 return operator_tensor(total_operator, qubits);
142 }
143
144 CmplxSpMat operator_tensor(
145 const OperatorSum &total_operator, const qubit_vector_t &qubits) {
146 CmplxSpMat sum = total_operator[0].second *
147 total_operator[0].first.to_sparse_matrix(qubits);
148
0/2
✗ Decision 'true' not taken.
✗ Decision 'false' not taken.
for (unsigned j = 1; j < total_operator.size(); j++) {
149 sum += total_operator[j].second *
150 total_operator[j].first.to_sparse_matrix(qubits);
151 }
152 return sum;
153 }
154
155 2 Eigen::VectorXcd QubitPauliString::dot_state(
156 const Eigen::VectorXcd &state) const {
157 2 const unsigned n_qubits = get_n_qb_from_statevector(state);
158
2/4
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
4 return (to_sparse_matrix(n_qubits) * state);
159 }
160
161 Eigen::VectorXcd QubitPauliString::dot_state(
162 const Eigen::VectorXcd &state, const qubit_vector_t &qubits) const {
163
0/2
✗ Decision 'true' not taken.
✗ Decision 'false' not taken.
if (state.size() != 1 << qubits.size())
164 throw std::logic_error(
165 "Size of statevector does not match number of qubits passed to "
166 "dot_state");
167 return (to_sparse_matrix(qubits) * state);
168 }
169
170 1 Complex QubitPauliString::state_expectation(
171 const Eigen::VectorXcd &state) const {
172
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 return state.dot(dot_state(state));
173 }
174
175 Complex QubitPauliString::state_expectation(
176 const Eigen::VectorXcd &state, const qubit_vector_t &qubits) const {
177 return state.dot(dot_state(state, qubits));
178 }
179
180 Complex operator_expectation(
181 const OperatorSum &total_operator, const Eigen::VectorXcd &state) {
182 Complex exp(0, 0);
183
184
0/2
✗ Decision 'true' not taken.
✗ Decision 'false' not taken.
for (unsigned j = 0; j < total_operator.size(); j++) {
185 exp += total_operator[j].second *
186 total_operator[j].first.state_expectation(state);
187 }
188
189 return exp;
190 }
191
192 Complex operator_expectation(
193 const OperatorSum &total_operator, const Eigen::VectorXcd &state,
194 const qubit_vector_t &qubits) {
195 Complex exp(0, 0);
196
197
0/2
✗ Decision 'true' not taken.
✗ Decision 'false' not taken.
for (unsigned j = 0; j < total_operator.size(); j++) {
198 exp += total_operator[j].second *
199 total_operator[j].first.state_expectation(state, qubits);
200 }
201
202 return exp;
203 }
204
205 85 QubitPauliString::QubitPauliString(const std::list<Pauli> &_paulis) {
206 85 unsigned qb_i = 0;
207
2/2
✓ Branch 4 taken 427 times.
✓ Branch 5 taken 85 times.
2/2
✓ Decision 'true' taken 427 times.
✓ Decision 'false' taken 85 times.
512 for (Pauli p : _paulis) {
208
2/4
✓ Branch 1 taken 427 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 427 times.
✗ Branch 5 not taken.
427 map[Qubit(qb_i)] = p;
209 427 ++qb_i;
210 }
211 85 }
212
213 76 QubitPauliString::QubitPauliString(
214 76 const std::list<Qubit> &qubits, const std::list<Pauli> &paulis) {
215
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 76 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 76 times.
76 if (qubits.size() != paulis.size()) {
216 throw std::logic_error(
217 "Mismatch of Qubits and Paulis upon QubitPauliString "
218 "construction");
219 }
220 76 std::list<Pauli>::const_iterator p_it = paulis.begin();
221
2/2
✓ Branch 5 taken 228 times.
✓ Branch 6 taken 76 times.
2/2
✓ Decision 'true' taken 228 times.
✓ Decision 'false' taken 76 times.
304 for (const Qubit &qb : qubits) {
222 228 Pauli p = *p_it;
223
2/4
✓ Branch 2 taken 228 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 228 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 228 times.
228 if (map.find(qb) != map.end()) {
224 throw std::logic_error(
225 "Non-unique Qubit inserted into QubitPauliString map");
226 }
227
1/2
✓ Branch 1 taken 228 times.
✗ Branch 2 not taken.
228 map[qb] = p;
228 228 ++p_it;
229 }
230 76 }
231
232 1533 bool QubitPauliString::operator==(const QubitPauliString &other) const {
233 1533 return compare(other) == 0;
234 }
235
236 bool QubitPauliString::operator!=(const QubitPauliString &other) const {
237 return !(*this == other);
238 }
239
240 735 bool QubitPauliString::operator<(const QubitPauliString &other) const {
241 735 return compare(other) < 0;
242 }
243
244 4316 int QubitPauliString::compare(const QubitPauliString &other) const {
245 4316 QubitPauliMap::const_iterator p1_it = this->map.begin();
246 4316 QubitPauliMap::const_iterator p2_it = other.map.begin();
247
2/2
✓ Branch 2 taken 6286 times.
✓ Branch 3 taken 1095 times.
2/2
✓ Decision 'true' taken 6286 times.
✓ Decision 'false' taken 1095 times.
7381 while (p1_it != this->map.end()) {
248
2/2
✓ Branch 1 taken 66 times.
✓ Branch 2 taken 6220 times.
2/2
✓ Decision 'true' taken 66 times.
✓ Decision 'false' taken 6220 times.
6286 if (p1_it->second == Pauli::I) {
249 66 ++p1_it;
250 66 continue;
251 }
252
6/6
✓ Branch 2 taken 6044 times.
✓ Branch 3 taken 192 times.
✓ Branch 5 taken 16 times.
✓ Branch 6 taken 6028 times.
✓ Branch 7 taken 16 times.
✓ Branch 8 taken 6220 times.
0/1
? Decision couldn't be analyzed.
6236 while (p2_it != other.map.end() && p2_it->second == Pauli::I) {
253 16 ++p2_it;
254 }
255
2/2
✓ Branch 2 taken 192 times.
✓ Branch 3 taken 6028 times.
2/2
✓ Decision 'true' taken 6028 times.
✓ Decision 'false' taken 192 times.
6220 if (p2_it == other.map.end()) return 1;
256 // QubitPauliString order should reflect ILO
257 // i.e. IZ < ZI (Zq1 < Zq0)
258 // Hence we first order by reverse of leading qubit
259
3/4
✓ Branch 3 taken 6028 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 599 times.
✓ Branch 6 taken 5429 times.
2/2
✓ Decision 'true' taken 5429 times.
✓ Decision 'false' taken 599 times.
6028 if (p1_it->first < p2_it->first) return 1;
260
3/4
✓ Branch 3 taken 5429 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 948 times.
✓ Branch 6 taken 4481 times.
2/2
✓ Decision 'true' taken 4481 times.
✓ Decision 'false' taken 948 times.
5429 if (p2_it->first < p1_it->first) return -1;
261 // and then by increasing order of Pauli letter on the same qubit
262
2/2
✓ Branch 2 taken 1076 times.
✓ Branch 3 taken 3405 times.
2/2
✓ Decision 'true' taken 3405 times.
✓ Decision 'false' taken 1076 times.
4481 if (p1_it->second < p2_it->second) return -1;
263
2/2
✓ Branch 2 taken 406 times.
✓ Branch 3 taken 2999 times.
2/2
✓ Decision 'true' taken 2999 times.
✓ Decision 'false' taken 406 times.
3405 if (p1_it->second > p2_it->second) return 1;
264 2999 ++p1_it;
265 2999 ++p2_it;
266 }
267
6/6
✓ Branch 2 taken 435 times.
✓ Branch 3 taken 675 times.
✓ Branch 5 taken 15 times.
✓ Branch 6 taken 420 times.
✓ Branch 7 taken 15 times.
✓ Branch 8 taken 1095 times.
0/1
? Decision couldn't be analyzed.
1110 while (p2_it != other.map.end() && p2_it->second == Pauli::I) {
268 15 ++p2_it;
269 }
270
2/2
✓ Branch 2 taken 675 times.
✓ Branch 3 taken 420 times.
1095 return (p2_it == other.map.end()) ? 0 : -1;
271 }
272
273 136 void QubitPauliString::compress() {
274 136 QubitPauliMap::iterator i = map.begin();
275
2/2
✓ Branch 2 taken 336 times.
✓ Branch 3 taken 136 times.
2/2
✓ Decision 'true' taken 336 times.
✓ Decision 'false' taken 136 times.
472 while (i != map.end()) {
276
2/2
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 321 times.
2/2
✓ Decision 'true' taken 15 times.
✓ Decision 'false' taken 321 times.
336 if (i->second == Pauli::I) {
277
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
15 i = map.erase(i);
278 } else {
279 321 ++i;
280 }
281 }
282 136 }
283
284 2022 bool QubitPauliString::commutes_with(const QubitPauliString &other) const {
285 2022 return (conflicting_qubits(other).size() % 2) == 0;
286 }
287
288 68 std::set<Qubit> QubitPauliString::common_qubits(
289 const QubitPauliString &other) const {
290 68 std::set<Qubit> common;
291
2/2
✓ Branch 5 taken 150 times.
✓ Branch 6 taken 68 times.
2/2
✓ Decision 'true' taken 150 times.
✓ Decision 'false' taken 68 times.
218 for (const std::pair<const Qubit, Pauli> &p : map) {
292
1/2
✓ Branch 1 taken 150 times.
✗ Branch 2 not taken.
150 QubitPauliMap::const_iterator found = other.map.find(p.first);
293
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 150 times.
1/2
✓ Decision 'true' taken 150 times.
✗ Decision 'false' not taken.
150 if (p.second == Pauli::I) continue;
294
6/6
✓ Branch 2 taken 132 times.
✓ Branch 3 taken 18 times.
✓ Branch 5 taken 69 times.
✓ Branch 6 taken 63 times.
✓ Branch 7 taken 69 times.
✓ Branch 8 taken 81 times.
2/2
✓ Decision 'true' taken 69 times.
✓ Decision 'false' taken 81 times.
150 if (found != other.map.end() && found->second == p.second) {
295
1/2
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
69 common.insert(p.first);
296 }
297 }
298 68 return common;
299 }
300
301 136 std::set<Qubit> QubitPauliString::own_qubits(
302 const QubitPauliString &other) const {
303 136 std::set<Qubit> own;
304
2/2
✓ Branch 5 taken 321 times.
✓ Branch 6 taken 136 times.
2/2
✓ Decision 'true' taken 321 times.
✓ Decision 'false' taken 136 times.
457 for (const std::pair<const Qubit, Pauli> &p : map) {
305
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 321 times.
1/2
✓ Decision 'true' taken 321 times.
✗ Decision 'false' not taken.
321 if (p.second == Pauli::I) continue;
306
1/2
✓ Branch 1 taken 321 times.
✗ Branch 2 not taken.
321 QubitPauliMap::const_iterator found = other.map.find(p.first);
307
5/6
✓ Branch 2 taken 264 times.
✓ Branch 3 taken 57 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 264 times.
✓ Branch 7 taken 57 times.
✓ Branch 8 taken 264 times.
2/2
✓ Decision 'true' taken 57 times.
✓ Decision 'false' taken 264 times.
321 if (found == other.map.end() || found->second == Pauli::I) {
308
1/2
✓ Branch 1 taken 57 times.
✗ Branch 2 not taken.
57 own.insert(p.first);
309 }
310 }
311 136 return own;
312 }
313
314 2159 std::set<Qubit> QubitPauliString::conflicting_qubits(
315 const QubitPauliString &other) const {
316 2159 std::set<Qubit> conflicts;
317
2/2
✓ Branch 5 taken 4636 times.
✓ Branch 6 taken 2159 times.
2/2
✓ Decision 'true' taken 4636 times.
✓ Decision 'false' taken 2159 times.
6795 for (const std::pair<const Qubit, Pauli> &p : map) {
318
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 4600 times.
2/2
✓ Decision 'true' taken 4600 times.
✓ Decision 'false' taken 36 times.
4636 if (p.second == Pauli::I) continue;
319
1/2
✓ Branch 1 taken 4600 times.
✗ Branch 2 not taken.
4600 QubitPauliMap::const_iterator found = other.map.find(p.first);
320
6/6
✓ Branch 2 taken 2534 times.
✓ Branch 3 taken 2066 times.
✓ Branch 5 taken 2504 times.
✓ Branch 6 taken 30 times.
✓ Branch 7 taken 1513 times.
✓ Branch 8 taken 3087 times.
2/2
✓ Decision 'true' taken 1513 times.
✓ Decision 'false' taken 5591 times.
7104 if (found != other.map.end() && found->second != Pauli::I &&
321
2/2
✓ Branch 1 taken 1513 times.
✓ Branch 2 taken 991 times.
2504 found->second != p.second) {
322
1/2
✓ Branch 1 taken 1513 times.
✗ Branch 2 not taken.
1513 conflicts.insert(p.first);
323 }
324 }
325 2159 return conflicts;
326 }
327
328 6 std::string QubitPauliString::to_str() const {
329
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 std::stringstream d;
330
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 d << "(";
331 6 QubitPauliMap::const_iterator i = map.begin();
332
2/2
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 6 times.
2/2
✓ Decision 'true' taken 7 times.
✓ Decision 'false' taken 6 times.
13 while (i != map.end()) {
333
3/5
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
7 switch (i->second) {
334
0/1
✗ Decision 'true' not taken.
case Pauli::I: {
335 d << "I";
336 break;
337 }
338
1/1
✓ Decision 'true' taken 1 times.
1 case Pauli::X: {
339
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 d << "X";
340 1 break;
341 }
342
1/1
✓ Decision 'true' taken 3 times.
3 case Pauli::Y: {
343
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 d << "Y";
344 3 break;
345 }
346
1/1
✓ Decision 'true' taken 3 times.
3 case Pauli::Z: {
347
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 d << "Z";
348 3 break;
349 }
350 }
351
2/4
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 7 times.
✗ Branch 6 not taken.
7 d << i->first.repr();
352 7 i++;
353
3/4
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 6 times.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
2/2
✓ Decision 'true' taken 6 times.
✓ Decision 'false' taken 1 times.
7 if (i != map.end()) d << ", ";
354 }
355
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 d << ")";
356
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
12 return d.str();
357 6 }
358
359 Pauli QubitPauliString::get(const Qubit &q) const {
360 QubitPauliMap::const_iterator i = map.find(q);
361
0/2
✗ Decision 'true' not taken.
✗ Decision 'false' not taken.
if (i == map.end())
362 return Pauli::I;
363 else
364 return i->second;
365 }
366
367 12 void QubitPauliString::set(const Qubit &q, Pauli p) {
368
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 QubitPauliMap::iterator i = map.find(q);
369
1/2
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
1/2
✓ Decision 'true' taken 12 times.
✗ Decision 'false' not taken.
12 if (i == map.end()) {
370
3/4
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 1 times.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 12 times.
12 if (p != Pauli::I) map.insert({q, p});
371 } else {
372
0/2
✗ Decision 'true' not taken.
✗ Decision 'false' not taken.
if (p == Pauli::I)
373 map.erase(i);
374 else
375 i->second = p;
376 }
377 12 }
378
379 189 std::size_t hash_value(const QubitPauliString &qps) {
380 189 size_t seed = 0;
381
2/2
✓ Branch 5 taken 513 times.
✓ Branch 6 taken 189 times.
2/2
✓ Decision 'true' taken 513 times.
✓ Decision 'false' taken 189 times.
702 for (const std::pair<const Qubit, Pauli> &qb_p : qps.map) {
382
2/2
✓ Branch 0 taken 494 times.
✓ Branch 1 taken 19 times.
2/2
✓ Decision 'true' taken 494 times.
✓ Decision 'false' taken 19 times.
513 if (qb_p.second != Pauli::I) {
383
1/2
✓ Branch 1 taken 494 times.
✗ Branch 2 not taken.
494 boost::hash_combine(seed, qb_p.first);
384
1/2
✓ Branch 1 taken 494 times.
✗ Branch 2 not taken.
494 boost::hash_combine(seed, qb_p.second);
385 }
386 }
387 189 return seed;
388 }
389
390 19 void to_json(nlohmann::json &j, const QubitPauliString &paulistr) {
391 19 j = nlohmann::json::array();
392
2/2
✓ Branch 6 taken 27 times.
✓ Branch 7 taken 19 times.
2/2
✓ Decision 'true' taken 27 times.
✓ Decision 'false' taken 19 times.
46 for (const auto &[qb, pauli] : paulistr.map) {
393
5/10
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 27 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 27 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 54 times.
✓ Branch 10 taken 27 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
81 j.push_back({qb, pauli});
394 }
395 19 }
396
397 8 void from_json(const nlohmann::json &j, QubitPauliString &paulistr) {
398
4/6
✓ Branch 3 taken 12 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 20 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 12 times.
✓ Branch 9 taken 8 times.
0/1
? Decision couldn't be analyzed.
20 for (const auto &qb_pauli : j) {
399
6/12
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 12 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 12 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 12 times.
✗ Branch 14 not taken.
✓ Branch 17 taken 12 times.
✗ Branch 18 not taken.
12 paulistr.set(qb_pauli[0].get<Qubit>(), qb_pauli[1].get<Pauli>());
400 }
401 8 }
402
403 585 const QubitPauliTensor::Mult_Matrix &QubitPauliTensor::get_mult_matrix() {
404 static const Mult_Matrix mult_matrix{
405 {{Pauli::I, Pauli::I}, {1., Pauli::I}},
406 {{Pauli::I, Pauli::X}, {1., Pauli::X}},
407 {{Pauli::I, Pauli::Y}, {1., Pauli::Y}},
408 {{Pauli::I, Pauli::Z}, {1., Pauli::Z}},
409 {{Pauli::X, Pauli::I}, {1., Pauli::X}},
410 {{Pauli::X, Pauli::X}, {1., Pauli::I}},
411 {{Pauli::X, Pauli::Y}, {i_, Pauli::Z}},
412 {{Pauli::X, Pauli::Z}, {-i_, Pauli::Y}},
413 {{Pauli::Y, Pauli::I}, {1., Pauli::Y}},
414 {{Pauli::Y, Pauli::X}, {-i_, Pauli::Z}},
415 {{Pauli::Y, Pauli::Y}, {1., Pauli::I}},
416 {{Pauli::Y, Pauli::Z}, {i_, Pauli::X}},
417 {{Pauli::Z, Pauli::I}, {1., Pauli::Z}},
418 {{Pauli::Z, Pauli::X}, {i_, Pauli::Y}},
419 {{Pauli::Z, Pauli::Y}, {-i_, Pauli::X}},
420
4/8
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 584 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.
585 {{Pauli::Z, Pauli::Z}, {1., Pauli::I}}};
421 585 return mult_matrix;
422 }
423
424 2567 QubitPauliTensor QubitPauliTensor::operator*(
425 const QubitPauliTensor &other) const {
426 2567 QubitPauliTensor result(this->coeff * other.coeff);
427 2567 QubitPauliMap::const_iterator p1i = this->string.map.begin();
428 2567 QubitPauliMap::const_iterator p2i = other.string.map.begin();
429
2/2
✓ Branch 2 taken 2346 times.
✓ Branch 3 taken 2567 times.
2/2
✓ Decision 'true' taken 2346 times.
✓ Decision 'false' taken 2567 times.
4913 while (p1i != this->string.map.end()) {
430
7/8
✓ Branch 2 taken 2328 times.
✓ Branch 3 taken 148 times.
✓ Branch 7 taken 2328 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 130 times.
✓ Branch 10 taken 2198 times.
✓ Branch 11 taken 130 times.
✓ Branch 12 taken 2346 times.
0/1
? Decision couldn't be analyzed.
2476 while (p2i != other.string.map.end() && p2i->first < p1i->first) {
431
1/2
✓ Branch 2 taken 130 times.
✗ Branch 3 not taken.
130 result.string.map.insert(*p2i);
432 130 p2i++;
433 }
434
7/8
✓ Branch 2 taken 2198 times.
✓ Branch 3 taken 148 times.
✓ Branch 7 taken 2198 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 579 times.
✓ Branch 10 taken 1619 times.
✓ Branch 11 taken 579 times.
✓ Branch 12 taken 1767 times.
2/2
✓ Decision 'true' taken 579 times.
✓ Decision 'false' taken 1767 times.
2346 if (p2i != other.string.map.end() && p2i->first == p1i->first) {
435 // Pauli in the same position, so need to multiply
436 const std::pair<Complex, Pauli> &prod =
437
2/4
✓ Branch 1 taken 579 times.
✗ Branch 2 not taken.
✓ Branch 7 taken 579 times.
✗ Branch 8 not taken.
579 QubitPauliTensor::get_mult_matrix().at({p1i->second, p2i->second});
438 579 result.coeff *= prod.first;
439
2/2
✓ Branch 0 taken 421 times.
✓ Branch 1 taken 158 times.
2/2
✓ Decision 'true' taken 421 times.
✓ Decision 'false' taken 158 times.
579 if (prod.second != Pauli::I) {
440
1/2
✓ Branch 3 taken 421 times.
✗ Branch 4 not taken.
421 result.string.map.insert({p1i->first, prod.second});
441 }
442 579 p2i++;
443 } else {
444
1/2
✓ Branch 2 taken 1767 times.
✗ Branch 3 not taken.
1767 result.string.map.insert(*p1i);
445 }
446 2346 p1i++;
447 }
448
2/2
✓ Branch 2 taken 2303 times.
✓ Branch 3 taken 2567 times.
2/2
✓ Decision 'true' taken 2303 times.
✓ Decision 'false' taken 2567 times.
4870 while (p2i != other.string.map.end()) {
449
1/2
✓ Branch 2 taken 2303 times.
✗ Branch 3 not taken.
2303 result.string.map.insert(*p2i);
450 2303 p2i++;
451 }
452 5134 return result;
453 }
454
455 221 bool QubitPauliTensor::operator==(const QubitPauliTensor &other) const {
456
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 217 times.
2/2
✓ Decision 'true' taken 217 times.
✓ Decision 'false' taken 4 times.
221 if (this->coeff != other.coeff) return false;
457 217 return (this->string == other.string);
458 }
459
460 163 bool QubitPauliTensor::operator!=(const QubitPauliTensor &other) const {
461 163 return !(*this == other);
462 }
463
464 2048 bool QubitPauliTensor::operator<(const QubitPauliTensor &other) const {
465 2048 int comp = this->string.compare(other.string);
466
2/2
✓ Branch 0 taken 1230 times.
✓ Branch 1 taken 818 times.
2/2
✓ Decision 'true' taken 818 times.
✓ Decision 'false' taken 1230 times.
2048 if (comp < 0) return true;
467
2/2
✓ Branch 0 taken 438 times.
✓ Branch 1 taken 380 times.
2/2
✓ Decision 'true' taken 380 times.
✓ Decision 'false' taken 438 times.
818 if (comp > 0) return false;
468
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 380 times.
1/2
✓ Decision 'true' taken 380 times.
✗ Decision 'false' not taken.
380 if (this->coeff.real() < other.coeff.real()) return true;
469
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 380 times.
1/2
✓ Decision 'true' taken 380 times.
✗ Decision 'false' not taken.
380 if (this->coeff.real() > other.coeff.real()) return false;
470 380 return (this->coeff.imag() < other.coeff.imag());
471 }
472
473 136 void QubitPauliTensor::compress() { string.compress(); }
474
475 1946 bool QubitPauliTensor::commutes_with(const QubitPauliTensor &other) const {
476 1946 return (string.commutes_with(other.string));
477 }
478
479 68 std::set<Qubit> QubitPauliTensor::common_qubits(
480 const QubitPauliTensor &other) const {
481 68 return string.common_qubits(other.string);
482 }
483
484 136 std::set<Qubit> QubitPauliTensor::own_qubits(
485 const QubitPauliTensor &other) const {
486 136 return string.own_qubits(other.string);
487 }
488
489 68 std::set<Qubit> QubitPauliTensor::conflicting_qubits(
490 const QubitPauliTensor &other) const {
491 68 return (string.conflicting_qubits(other.string));
492 }
493
494 6 std::string QubitPauliTensor::to_str() const {
495
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 std::stringstream d;
496
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4 times.
2/2
✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 4 times.
6 if (coeff == -1.) {
497
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 d << "-";
498
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 4 times.
4 } else if (coeff != 1.) {
499 d << coeff << "*";
500 }
501
2/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
6 d << string.to_str();
502
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
12 return d.str();
503 6 }
504
505 6 std::size_t hash_value(const QubitPauliTensor &qpt) {
506
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 size_t seed = hash_value(qpt.string);
507
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 boost::hash_combine(seed, qpt.coeff);
508 6 return seed;
509 }
510
511 254 QubitPauliTensor operator*(Complex a, const QubitPauliTensor &qpt) {
512 254 QubitPauliTensor result = qpt;
513 254 result.coeff *= a;
514 254 return result;
515 }
516
517 101 PauliStabiliser::PauliStabiliser(
518 101 const std::vector<Pauli> string, const bool coeff)
519 101 : string(string), coeff(coeff) {
520
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 101 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 101 times.
101 if (string.size() == 0) {
521 throw std::invalid_argument("Pauli stabiliser cannot be empty.");
522 }
523
1/2
✓ Branch 3 taken 101 times.
✗ Branch 4 not taken.
2/2
✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 99 times.
101 if (std::adjacent_find(string.begin(), string.end(), std::not_equal_to<>()) ==
524
4/4
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 85 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 99 times.
218 string.end() &&
525
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 14 times.
16 string[0] == Pauli::I) {
526
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 throw std::invalid_argument("Pauli stabiliser cannot be identity.");
527 }
528 101 }
529
530 5 bool PauliStabiliser::operator==(const PauliStabiliser &other) const {
531
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
5 return coeff == other.coeff && string == other.string;
532 }
533
534 bool PauliStabiliser::operator!=(const PauliStabiliser &other) const {
535 return coeff != other.coeff || string != other.string;
536 }
537
538 5 void to_json(nlohmann::json &j, const PauliStabiliser &pauli_stabiliser) {
539
1/2
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 j["string"] = pauli_stabiliser.string;
540
1/2
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 j["coeff"] = pauli_stabiliser.coeff;
541 5 }
542
543 5 void from_json(const nlohmann::json &j, PauliStabiliser &pauli_stabiliser) {
544
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
10 pauli_stabiliser = PauliStabiliser(
545 15 j.at("string").get<std::vector<Pauli>>(), j.at("coeff").get<bool>());
546 5 }
547 } // namespace tket
548