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 "MatrixAnalysis.hpp" | |||
16 | ||||
17 | #include <fstream> | |||
18 | #include <iostream> | |||
19 | #include <limits> | |||
20 | #include <map> | |||
21 | #include <optional> | |||
22 | #include <sstream> | |||
23 | #include <tkassert/Assert.hpp> | |||
24 | #include <utility> | |||
25 | #include <vector> | |||
26 | ||||
27 | #include "Utils/EigenConfig.hpp" | |||
28 | ||||
29 | namespace tket { | |||
30 | ||||
31 | 12159 | bool is_unitary(const Eigen::MatrixXcd &U, double tol) { | ||
32 | 12159 | unsigned m = U.rows(), n = U.cols(); | ||
33 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12159 times.
|
1/2✓ Decision 'true' taken 12159 times.
✗ Decision 'false' not taken.
|
12159 | if (m != n) return false; |
34 |
3/6✓ Branch 2 taken 12159 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 12159 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 12159 times.
✗ Branch 9 not taken.
|
12159 | return Eigen::MatrixXcd::Identity(n, n).isApprox(U.adjoint() * U, tol); | |
35 | } | |||
36 | ||||
37 | 2 | bool is_projector(const Eigen::MatrixXcd &P, double tol) { | ||
38 | 2 | unsigned m = P.rows(), n = P.cols(); | ||
39 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
1/2✓ Decision 'true' taken 2 times.
✗ Decision 'false' not taken.
|
2 | if (m != n) return false; |
40 |
6/12✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 2 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 2 times.
✗ Branch 16 not taken.
|
2 | return P.isApprox(P * P, tol) && P.isApprox(P.adjoint()); | |
41 | } | |||
42 | ||||
43 | 86 | std::pair<MatrixXb, MatrixXb> binary_LLT_decomposition(const MatrixXb &a) { | ||
44 | /* | |||
45 | * Aaronson-Gottesman: Improved Simulation of Stabilizer Circuits, Lemma 7 | |||
46 | * For any symmetric, binary matrix A, there exists a diagonal matrix D | |||
47 | * and an invertible lower-triangular matrix L such that A + D = LL^T. | |||
48 | * Given A, returns the pair <L, D> | |||
49 | */ | |||
50 | 86 | unsigned n = a.rows(); | ||
51 |
2/4✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 86 times.
✗ Branch 5 not taken.
|
86 | MatrixXb lo = MatrixXb::Identity(n, n); | |
52 |
2/2✓ Branch 0 taken 278 times.
✓ Branch 1 taken 86 times.
|
2/2✓ Decision 'true' taken 278 times.
✓ Decision 'false' taken 86 times.
|
364 | for (unsigned j = 0; j < n; j++) { |
53 |
2/2✓ Branch 0 taken 338 times.
✓ Branch 1 taken 278 times.
|
2/2✓ Decision 'true' taken 338 times.
✓ Decision 'false' taken 278 times.
|
616 | for (unsigned i = j + 1; i < n; i++) { |
54 |
1/2✓ Branch 1 taken 338 times.
✗ Branch 2 not taken.
|
338 | bool sum = a(i, j); | |
55 |
2/2✓ Branch 0 taken 190 times.
✓ Branch 1 taken 338 times.
|
2/2✓ Decision 'true' taken 190 times.
✓ Decision 'false' taken 338 times.
|
528 | for (unsigned k = 0; k < j; k++) { |
56 |
5/8✓ Branch 1 taken 190 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 180 times.
✓ Branch 6 taken 10 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 10 times.
✗ Branch 9 not taken.
|
190 | sum ^= lo(i, k) && lo(j, k); | |
57 | } | |||
58 |
1/2✓ Branch 1 taken 338 times.
✗ Branch 2 not taken.
|
338 | lo(i, j) = sum; | |
59 | } | |||
60 | } | |||
61 |
2/4✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 86 times.
✗ Branch 5 not taken.
|
86 | MatrixXb d = MatrixXb::Zero(n, n); | |
62 |
2/2✓ Branch 0 taken 278 times.
✓ Branch 1 taken 86 times.
|
2/2✓ Decision 'true' taken 278 times.
✓ Decision 'false' taken 86 times.
|
364 | for (unsigned i = 0; i < n; i++) { |
63 |
1/2✓ Branch 1 taken 278 times.
✗ Branch 2 not taken.
|
278 | bool sum = a(i, i); | |
64 |
2/2✓ Branch 0 taken 954 times.
✓ Branch 1 taken 278 times.
|
2/2✓ Decision 'true' taken 954 times.
✓ Decision 'false' taken 278 times.
|
1232 | for (unsigned k = 0; k < n; k++) { |
65 |
1/2✓ Branch 1 taken 954 times.
✗ Branch 2 not taken.
|
954 | sum ^= lo(i, k); | |
66 | } // diagonal element of LL^T is just parity of row of L | |||
67 |
1/2✓ Branch 1 taken 278 times.
✗ Branch 2 not taken.
|
278 | d(i, i) = sum; | |
68 | } | |||
69 |
1/2✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
|
172 | return {lo, d}; | |
70 | 86 | } | ||
71 | ||||
72 | 215 | std::vector<std::pair<unsigned, unsigned>> gaussian_elimination_col_ops( | ||
73 | const MatrixXb &a, unsigned blocksize) { | |||
74 |
2/4✓ Branch 2 taken 215 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 215 times.
✗ Branch 6 not taken.
|
215 | return gaussian_elimination_row_ops(a.transpose(), blocksize); | |
75 | } | |||
76 | ||||
77 | struct MatrixXbBlockCompare { | |||
78 | 22038 | bool operator()( | ||
79 | const MatrixXb::BlockXpr &lhs, const MatrixXb::BlockXpr &rhs) const { | |||
80 | TKET_ASSERT(lhs.rows() == rhs.rows()); | |||
81 | TKET_ASSERT(lhs.cols() == rhs.cols()); | |||
82 |
2/2✓ Branch 1 taken 22038 times.
✓ Branch 2 taken 406 times.
|
2/2✓ Decision 'true' taken 22038 times.
✓ Decision 'false' taken 406 times.
|
22444 | for (unsigned r = 0; r < lhs.rows(); ++r) { |
83 |
2/2✓ Branch 1 taken 49988 times.
✓ Branch 2 taken 406 times.
|
2/2✓ Decision 'true' taken 49988 times.
✓ Decision 'false' taken 406 times.
|
50394 | for (unsigned c = 0; c < lhs.cols(); ++c) { |
84 |
2/2✓ Branch 2 taken 12596 times.
✓ Branch 3 taken 37392 times.
|
2/2✓ Decision 'true' taken 37392 times.
✓ Decision 'false' taken 12596 times.
|
49988 | if (lhs(r, c) < rhs(r, c)) return true; |
85 |
2/2✓ Branch 2 taken 9036 times.
✓ Branch 3 taken 28356 times.
|
2/2✓ Decision 'true' taken 406 times.
✓ Decision 'false' taken 36986 times.
|
37392 | if (lhs(r, c) > rhs(r, c)) return false; |
86 | } | |||
87 | } | |||
88 | 406 | return false; | ||
89 | } | |||
90 | }; | |||
91 | ||||
92 | /* see https://web.eecs.umich.edu/~imarkov/pubs/jour/qic08-cnot.pdf for a full | |||
93 | * explanation of this technique */ | |||
94 | /* K. Patel, I. Markov, J. Hayes. Optimal Synthesis of Linear Reversible | |||
95 | Circuits. QIC 2008 */ | |||
96 | 532 | std::vector<std::pair<unsigned, unsigned>> gaussian_elimination_row_ops( | ||
97 | const MatrixXb &a, unsigned blocksize) { | |||
98 | 532 | std::vector<std::pair<unsigned, unsigned>> ops; | ||
99 |
1/2✓ Branch 1 taken 532 times.
✗ Branch 2 not taken.
|
532 | MatrixXb m = a; | |
100 | 532 | unsigned rows = m.rows(); | ||
101 | 532 | unsigned cols = m.cols(); | ||
102 | 532 | std::vector<unsigned> pcols; // we know which columns have non-zero entries | ||
103 | // (to save actually transposing the matrix) | |||
104 | 532 | unsigned pivot_row = 0; | ||
105 | 532 | unsigned ceiling = | ||
106 | 532 | (cols + blocksize - 1) / blocksize; // ceil(cols/blocksize) | ||
107 | ||||
108 | // Get to upper echelon form | |||
109 |
2/2✓ Branch 0 taken 645 times.
✓ Branch 1 taken 532 times.
|
2/2✓ Decision 'true' taken 645 times.
✓ Decision 'false' taken 532 times.
|
1177 | for (unsigned sec = 0; sec < ceiling; ++sec) { |
110 | // determine column range for this section | |||
111 | // if it hits cols don't go any higher | |||
112 | 645 | unsigned i0 = sec * blocksize; | ||
113 | 645 | unsigned i1 = std::min(cols, (sec + 1) * blocksize); | ||
114 | ||||
115 | /* first, try to eliminate sub-rows (ie chunks), for greater speed than | |||
116 | * naively doing gaussian elim. */ | |||
117 | 645 | std::map<MatrixXb::BlockXpr, unsigned, MatrixXbBlockCompare> chunks; | ||
118 |
2/2✓ Branch 0 taken 3053 times.
✓ Branch 1 taken 645 times.
|
2/2✓ Decision 'true' taken 3053 times.
✓ Decision 'false' taken 645 times.
|
3698 | for (unsigned r = pivot_row; r < rows; ++r) { |
119 |
1/2✓ Branch 1 taken 3053 times.
✗ Branch 2 not taken.
|
3053 | MatrixXb::BlockXpr t = m.block(r, i0, 1, i1 - i0); | |
120 |
4/6✓ Branch 1 taken 3053 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3053 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 413 times.
✓ Branch 7 taken 2640 times.
|
2/2✓ Decision 'true' taken 2640 times.
✓ Decision 'false' taken 413 times.
|
3053 | if (t == MatrixXb::Zero(1, i1 - i0)) continue; |
121 | /* if first copy of pattern, save. If duplicate then remove by adding | |||
122 | * rows*/ | |||
123 |
1/2✓ Branch 1 taken 2640 times.
✗ Branch 2 not taken.
|
2640 | auto chunk_it = chunks.find(t); | |
124 |
2/2✓ Branch 2 taken 152 times.
✓ Branch 3 taken 2488 times.
|
2/2✓ Decision 'true' taken 1937 times.
✓ Decision 'false' taken 703 times.
|
2640 | if (chunk_it != chunks.end()) { |
125 |
2/2✓ Branch 0 taken 1785 times.
✓ Branch 1 taken 152 times.
|
2/2✓ Decision 'true' taken 1785 times.
✓ Decision 'false' taken 152 times.
|
1937 | for (unsigned k = 0; k < cols; ++k) { |
126 |
2/4✓ Branch 2 taken 1785 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1785 times.
✗ Branch 6 not taken.
|
1785 | m(r, k) ^= m(chunk_it->second, k); | |
127 | } | |||
128 |
1/2✓ Branch 3 taken 152 times.
✗ Branch 4 not taken.
|
152 | ops.push_back({chunk_it->second, r}); | |
129 | } else { | |||
130 |
1/2✓ Branch 2 taken 2488 times.
✗ Branch 3 not taken.
|
2488 | chunks.insert({t, r}); | |
131 | } | |||
132 | } | |||
133 | /* do gaussian elim. on remaining entries */ | |||
134 |
2/2✓ Branch 0 taken 2591 times.
✓ Branch 1 taken 645 times.
|
2/2✓ Decision 'true' taken 2591 times.
✓ Decision 'false' taken 645 times.
|
3236 | for (unsigned col = i0; col < i1; ++col) { |
135 | // find first 1 element in column after pivot_row | |||
136 | 2591 | unsigned first_1 = pivot_row; | ||
137 |
7/8✓ Branch 0 taken 3022 times.
✓ Branch 1 taken 113 times.
✓ Branch 3 taken 3022 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 544 times.
✓ Branch 6 taken 2478 times.
✓ Branch 7 taken 544 times.
✓ Branch 8 taken 2591 times.
|
0/1? Decision couldn't be analyzed.
|
3135 | while (first_1 < rows && !m(first_1, col)) { |
138 | 544 | ++first_1; | ||
139 | } | |||
140 |
2/2✓ Branch 0 taken 113 times.
✓ Branch 1 taken 2478 times.
|
2/2✓ Decision 'true' taken 2478 times.
✓ Decision 'false' taken 113 times.
|
2591 | if (first_1 == rows) continue; |
141 | ||||
142 | // pull back to pivot | |||
143 |
2/2✓ Branch 0 taken 116 times.
✓ Branch 1 taken 2362 times.
|
2/2✓ Decision 'true' taken 767 times.
✓ Decision 'false' taken 1711 times.
|
2478 | if (first_1 != pivot_row) { |
144 |
2/2✓ Branch 0 taken 651 times.
✓ Branch 1 taken 116 times.
|
2/2✓ Decision 'true' taken 651 times.
✓ Decision 'false' taken 116 times.
|
767 | for (unsigned k = 0; k < cols; ++k) { |
145 |
2/4✓ Branch 1 taken 651 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 651 times.
✗ Branch 5 not taken.
|
651 | m(pivot_row, k) ^= m(first_1, k); | |
146 | } | |||
147 |
1/2✓ Branch 2 taken 116 times.
✗ Branch 3 not taken.
|
116 | ops.push_back({first_1, pivot_row}); | |
148 | } | |||
149 | ||||
150 | // clear all entries below pivot | |||
151 |
2/2✓ Branch 1 taken 7116 times.
✓ Branch 2 taken 2478 times.
|
2/2✓ Decision 'true' taken 7116 times.
✓ Decision 'false' taken 2478 times.
|
9594 | for (unsigned r = std::max(pivot_row + 1, first_1); r < rows; ++r) { |
152 |
3/4✓ Branch 1 taken 7116 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 772 times.
✓ Branch 4 taken 6344 times.
|
2/2✓ Decision 'true' taken 5920 times.
✓ Decision 'false' taken 1196 times.
|
7116 | if (m(r, col)) { |
153 |
2/2✓ Branch 0 taken 5148 times.
✓ Branch 1 taken 772 times.
|
2/2✓ Decision 'true' taken 5148 times.
✓ Decision 'false' taken 772 times.
|
5920 | for (unsigned k = 0; k < cols; ++k) { |
154 |
2/4✓ Branch 1 taken 5148 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5148 times.
✗ Branch 5 not taken.
|
5148 | m(r, k) ^= m(pivot_row, k); | |
155 | } | |||
156 |
1/2✓ Branch 2 taken 772 times.
✗ Branch 3 not taken.
|
772 | ops.push_back({pivot_row, r}); | |
157 | } | |||
158 | } | |||
159 | ||||
160 | // record that we pivoted for this column | |||
161 |
1/2✓ Branch 1 taken 2478 times.
✗ Branch 2 not taken.
|
2478 | pcols.push_back(col); | |
162 | 2478 | ++pivot_row; | ||
163 | } | |||
164 | 645 | } | ||
165 | ||||
166 | /* matrix is now in upper triangular form; matrix is reduced to diagonal */ | |||
167 | ||||
168 | 532 | --pivot_row; | ||
169 | ||||
170 |
2/2✓ Branch 0 taken 645 times.
✓ Branch 1 taken 532 times.
|
2/2✓ Decision 'true' taken 645 times.
✓ Decision 'false' taken 532 times.
|
1177 | for (unsigned sec = ceiling; sec-- > 0;) { |
171 | 645 | unsigned i0 = sec * blocksize; | ||
172 | 645 | unsigned i1 = std::min(cols, (sec + 1) * blocksize); | ||
173 | ||||
174 | 645 | std::map<MatrixXb::BlockXpr, unsigned, MatrixXbBlockCompare> chunks; | ||
175 |
2/2✓ Branch 0 taken 3120 times.
✓ Branch 1 taken 645 times.
|
2/2✓ Decision 'true' taken 3120 times.
✓ Decision 'false' taken 645 times.
|
3765 | for (unsigned r = pivot_row + 1; r-- > 0;) { |
176 |
1/2✓ Branch 1 taken 3120 times.
✗ Branch 2 not taken.
|
3120 | MatrixXb::BlockXpr t = m.block(r, i0, 1, i1 - i0); | |
177 |
4/6✓ Branch 1 taken 3120 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3120 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 557 times.
✓ Branch 7 taken 2563 times.
|
2/2✓ Decision 'true' taken 2563 times.
✓ Decision 'false' taken 557 times.
|
3120 | if (t == MatrixXb::Zero(1, i1 - i0)) continue; |
178 | ||||
179 |
1/2✓ Branch 1 taken 2563 times.
✗ Branch 2 not taken.
|
2563 | auto chunk_it = chunks.find(t); | |
180 |
2/2✓ Branch 2 taken 51 times.
✓ Branch 3 taken 2512 times.
|
2/2✓ Decision 'true' taken 672 times.
✓ Decision 'false' taken 1891 times.
|
2563 | if (chunk_it != chunks.end()) { |
181 |
2/2✓ Branch 0 taken 621 times.
✓ Branch 1 taken 51 times.
|
2/2✓ Decision 'true' taken 621 times.
✓ Decision 'false' taken 51 times.
|
672 | for (unsigned k = 0; k < cols; ++k) { |
182 |
2/4✓ Branch 2 taken 621 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 621 times.
✗ Branch 6 not taken.
|
621 | m(r, k) ^= m(chunk_it->second, k); | |
183 | } | |||
184 |
1/2✓ Branch 3 taken 51 times.
✗ Branch 4 not taken.
|
51 | ops.push_back({chunk_it->second, r}); | |
185 | } else { | |||
186 |
1/2✓ Branch 2 taken 2512 times.
✗ Branch 3 not taken.
|
2512 | chunks.insert({t, r}); | |
187 | } | |||
188 | } | |||
189 |
7/8✓ Branch 1 taken 2582 times.
✓ Branch 2 taken 541 times.
✓ Branch 4 taken 2478 times.
✓ Branch 5 taken 104 times.
✓ Branch 7 taken 2478 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 2478 times.
✓ Branch 10 taken 645 times.
|
0/1? Decision couldn't be analyzed.
|
3123 | while (!pcols.empty() && i0 <= pcols.back() && pcols.back() < i1) { |
190 | 2478 | unsigned pcol = pcols.back(); | ||
191 | 2478 | pcols.pop_back(); | ||
192 |
2/2✓ Branch 0 taken 6877 times.
✓ Branch 1 taken 2478 times.
|
2/2✓ Decision 'true' taken 6877 times.
✓ Decision 'false' taken 2478 times.
|
9355 | for (unsigned r = 0; r < pivot_row; ++r) { |
193 |
3/4✓ Branch 1 taken 6877 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 632 times.
✓ Branch 4 taken 6245 times.
|
2/2✓ Decision 'true' taken 4960 times.
✓ Decision 'false' taken 1917 times.
|
6877 | if (m(r, pcol)) { |
194 |
2/2✓ Branch 0 taken 4328 times.
✓ Branch 1 taken 632 times.
|
2/2✓ Decision 'true' taken 4328 times.
✓ Decision 'false' taken 632 times.
|
4960 | for (unsigned k = 0; k < cols; ++k) { |
195 |
2/4✓ Branch 1 taken 4328 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4328 times.
✗ Branch 5 not taken.
|
4328 | m(r, k) ^= m(pivot_row, k); | |
196 | } | |||
197 |
1/2✓ Branch 2 taken 632 times.
✗ Branch 3 not taken.
|
632 | ops.push_back({pivot_row, r}); | |
198 | } | |||
199 | } | |||
200 | 2478 | --pivot_row; | ||
201 | } | |||
202 | 645 | } | ||
203 | ||||
204 | 1064 | return ops; | ||
205 | 532 | } | ||
206 | ||||
207 | 3 | static Eigen::PermutationMatrix<Eigen::Dynamic> qubit_permutation( | ||
208 | unsigned n_qubits) { | |||
209 | 3 | Eigen::PermutationMatrix<Eigen::Dynamic> perm(1u << n_qubits); | ||
210 |
2/2✓ Branch 0 taken 20 times.
✓ Branch 1 taken 3 times.
|
2/2✓ Decision 'true' taken 20 times.
✓ Decision 'false' taken 3 times.
|
23 | for (unsigned i = 0; i < (1u << n_qubits); i++) { |
211 | 20 | unsigned rev = 0; | ||
212 | 20 | unsigned forwards = i; | ||
213 |
2/2✓ Branch 0 taken 56 times.
✓ Branch 1 taken 20 times.
|
2/2✓ Decision 'true' taken 56 times.
✓ Decision 'false' taken 20 times.
|
76 | for (unsigned q = 0; q < n_qubits; q++) { |
214 | 56 | rev <<= 1; | ||
215 | 56 | rev += forwards % 2; | ||
216 | 56 | forwards >>= 1; | ||
217 | } | |||
218 |
1/2✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
|
20 | perm.indices()[i] = rev; | |
219 | } | |||
220 | 3 | return perm; | ||
221 | } | |||
222 | ||||
223 | 5868 | Eigen::PermutationMatrix<Eigen::Dynamic> lift_perm( | ||
224 | const std::map<unsigned, unsigned> &p) { | |||
225 | 5868 | unsigned n = p.size(); | ||
226 | 5868 | Eigen::PermutationMatrix<Eigen::Dynamic> pm(1u << n); | ||
227 |
2/2✓ Branch 0 taken 129346 times.
✓ Branch 1 taken 5868 times.
|
2/2✓ Decision 'true' taken 129346 times.
✓ Decision 'false' taken 5868 times.
|
135214 | for (unsigned i = 0; i < (1u << n); ++i) { |
228 | 129346 | unsigned target = 0; | ||
229 | 129346 | unsigned mask = 1u << n; | ||
230 |
2/2✓ Branch 0 taken 907740 times.
✓ Branch 1 taken 129346 times.
|
2/2✓ Decision 'true' taken 907740 times.
✓ Decision 'false' taken 129346 times.
|
1037086 | for (unsigned q = 0; q < n; ++q) { |
231 | 907740 | mask /= 2; | ||
232 |
2/2✓ Branch 0 taken 453870 times.
✓ Branch 1 taken 453870 times.
|
2/2✓ Decision 'true' taken 453870 times.
✓ Decision 'false' taken 453870 times.
|
907740 | if (i & mask) { |
233 |
1/2✓ Branch 1 taken 453870 times.
✗ Branch 2 not taken.
|
453870 | target |= 1u << (n - 1 - p.at(q)); | |
234 | } | |||
235 | } | |||
236 |
1/2✓ Branch 2 taken 129346 times.
✗ Branch 3 not taken.
|
129346 | pm.indices()[i] = target; | |
237 | } | |||
238 | 5868 | return pm; | ||
239 | } | |||
240 | ||||
241 | 5868 | static Eigen::PermutationMatrix<Eigen::Dynamic> qubit_permutation( | ||
242 | const qubit_map_t &qmap) { | |||
243 | 5868 | std::map<Qubit, unsigned> q_indices; | ||
244 | 5868 | std::map<unsigned, unsigned> uq_map; | ||
245 | 5868 | unsigned qi = 0; | ||
246 |
2/2✓ Branch 4 taken 16188 times.
✓ Branch 5 taken 5868 times.
|
2/2✓ Decision 'true' taken 16188 times.
✓ Decision 'false' taken 5868 times.
|
22056 | for (const std::pair<const Qubit, Qubit> &pair : qmap) { |
247 |
1/2✓ Branch 2 taken 16188 times.
✗ Branch 3 not taken.
|
16188 | q_indices.insert({pair.first, qi}); | |
248 | 16188 | ++qi; | ||
249 | } | |||
250 |
2/2✓ Branch 4 taken 16188 times.
✓ Branch 5 taken 5868 times.
|
2/2✓ Decision 'true' taken 16188 times.
✓ Decision 'false' taken 5868 times.
|
22056 | for (const std::pair<const Qubit, Qubit> &pair : qmap) { |
251 |
1/2✓ Branch 1 taken 16188 times.
✗ Branch 2 not taken.
|
16188 | unsigned in = q_indices[pair.first]; | |
252 |
1/2✓ Branch 1 taken 16188 times.
✗ Branch 2 not taken.
|
16188 | unsigned out = q_indices[pair.second]; | |
253 |
1/2✓ Branch 2 taken 16188 times.
✗ Branch 3 not taken.
|
16188 | uq_map.insert({in, out}); | |
254 | } | |||
255 |
1/2✓ Branch 1 taken 5868 times.
✗ Branch 2 not taken.
|
11736 | return lift_perm(uq_map); | |
256 | 5868 | } | ||
257 | ||||
258 | static const Eigen::PermutationMatrix<4, 4> SWAP = qubit_permutation(2); | |||
259 | ||||
260 | 1 | Eigen::Matrix4cd reverse_indexing(const Eigen::Matrix4cd &m) { | ||
261 |
2/4✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
|
2 | return SWAP * m * SWAP; | |
262 | } | |||
263 | ||||
264 | ✗ | Matrix8cd reverse_indexing(const Matrix8cd &m) { | ||
265 | return static_cast<const Matrix8cd &>( | |||
266 | ✗ | reverse_indexing(static_cast<const Eigen::MatrixXcd &>(m))); | ||
267 | } | |||
268 | ||||
269 | 1 | Eigen::MatrixXcd reverse_indexing(const Eigen::MatrixXcd &m) { | ||
270 | 1 | unsigned dim = m.rows(); | ||
271 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | unsigned n_qubits = get_number_of_qubits(dim); | |
272 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | Eigen::PermutationMatrix<Eigen::Dynamic> perm = qubit_permutation(n_qubits); | |
273 |
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 | return perm * m * perm; | |
274 | 1 | } | ||
275 | ||||
276 | 1 | Eigen::VectorXcd reverse_indexing(const Eigen::VectorXcd &v) { | ||
277 | 1 | unsigned dim = v.size(); | ||
278 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | unsigned n_qubits = get_number_of_qubits(dim); | |
279 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | Eigen::PermutationMatrix<Eigen::Dynamic> perm = qubit_permutation(n_qubits); | |
280 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
2 | return perm * v; | |
281 | 1 | } | ||
282 | ||||
283 | 5868 | Eigen::MatrixXcd apply_qubit_permutation( | ||
284 | const Eigen::MatrixXcd &m, const qubit_map_t &perm) { | |||
285 |
1/2✓ Branch 1 taken 5868 times.
✗ Branch 2 not taken.
|
5868 | Eigen::PermutationMatrix<Eigen::Dynamic> perm_m = qubit_permutation(perm); | |
286 |
2/4✓ Branch 1 taken 5868 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5868 times.
✗ Branch 5 not taken.
|
11736 | return perm_m * m; | |
287 | 5868 | } | ||
288 | ||||
289 | ✗ | Eigen::VectorXcd apply_qubit_permutation( | ||
290 | const Eigen::VectorXcd &v, const qubit_map_t &perm) { | |||
291 | ✗ | Eigen::PermutationMatrix<Eigen::Dynamic> perm_m = qubit_permutation(perm); | ||
292 | ✗ | return perm_m * v; | ||
293 | } | |||
294 | ||||
295 | 866 | double trace_fidelity(double a, double b, double c) { | ||
296 | 866 | constexpr double g = PI / 2; | ||
297 | 866 | a *= g; | ||
298 | 866 | b *= g; | ||
299 | 866 | c *= g; | ||
300 | 866 | double trace_sq = 16. * (pow(cos(a) * cos(b) * cos(c), 2) + | ||
301 | 866 | pow(sin(a) * sin(b) * sin(c), 2)); | ||
302 | 866 | return (4. + trace_sq) / 20.; | ||
303 | } | |||
304 | ||||
305 | 9951 | inline double mod(double d, double max) { return d - max * floor(d / max); } | ||
306 | ||||
307 | std::tuple<Eigen::Matrix4cd, std::array<double, 3>, Eigen::Matrix4cd> | |||
308 | 3317 | get_information_content(const Eigen::Matrix4cd &X) { | ||
309 | using ExpGate = std::array<double, 3>; | |||
310 | using Mat4 = Eigen::Matrix4cd; | |||
311 | using Vec4 = Eigen::Vector4cd; | |||
312 | ||||
313 |
3/6✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3317 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 3317 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 3317 times.
|
3317 | if (!is_unitary(X)) { |
314 | ✗ | throw std::invalid_argument( | ||
315 | ✗ | "Non-unitary matrix passed to get_information_content"); | ||
316 | } | |||
317 | ||||
318 |
3/6✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3317 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3317 times.
✗ Branch 8 not taken.
|
3317 | Eigen::Matrix2cd PauliX, PauliY, PauliZ; | |
319 |
4/8✓ Branch 2 taken 3317 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 3317 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 3317 times.
✗ Branch 11 not taken.
✓ Branch 14 taken 3317 times.
✗ Branch 15 not taken.
|
3317 | PauliX << 0, 1, 1, 0; | |
320 |
4/8✓ Branch 2 taken 3317 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 3317 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 3317 times.
✗ Branch 10 not taken.
✓ Branch 13 taken 3317 times.
✗ Branch 14 not taken.
|
3317 | PauliY << 0, -i_, i_, 0; | |
321 |
4/8✓ Branch 2 taken 3317 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 3317 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 3317 times.
✗ Branch 11 not taken.
✓ Branch 14 taken 3317 times.
✗ Branch 15 not taken.
|
3317 | PauliZ << 1, 0, 0, -1; | |
322 | ||||
323 | // change of basis for SU(2) x SU(2) -> SO(4) | |||
324 |
1/2✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
|
3317 | Mat4 MagicM; | |
325 |
16/32✓ Branch 2 taken 3317 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 3317 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 3317 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 3317 times.
✗ Branch 14 not taken.
✓ Branch 17 taken 3317 times.
✗ Branch 18 not taken.
✓ Branch 20 taken 3317 times.
✗ Branch 21 not taken.
✓ Branch 24 taken 3317 times.
✗ Branch 25 not taken.
✓ Branch 28 taken 3317 times.
✗ Branch 29 not taken.
✓ Branch 32 taken 3317 times.
✗ Branch 33 not taken.
✓ Branch 35 taken 3317 times.
✗ Branch 36 not taken.
✓ Branch 39 taken 3317 times.
✗ Branch 40 not taken.
✓ Branch 43 taken 3317 times.
✗ Branch 44 not taken.
✓ Branch 47 taken 3317 times.
✗ Branch 48 not taken.
✓ Branch 51 taken 3317 times.
✗ Branch 52 not taken.
✓ Branch 55 taken 3317 times.
✗ Branch 56 not taken.
✓ Branch 59 taken 3317 times.
✗ Branch 60 not taken.
|
3317 | MagicM << 1, 0, 0, i_, 0, i_, 1, 0, 0, i_, -1, 0, 1, 0, 0, -i_; | |
326 |
1/2✓ Branch 2 taken 3317 times.
✗ Branch 3 not taken.
|
3317 | MagicM /= sqrt(2.); // unitary | |
327 | ||||
328 | // change to magic basis and make sure U in SU(4) | |||
329 |
2/4✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3317 times.
✗ Branch 5 not taken.
|
3317 | const auto norm_X = pow(X.determinant(), 0.25); | |
330 |
5/10✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3317 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3317 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3317 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 3317 times.
✗ Branch 14 not taken.
|
3317 | const Mat4 Xprime = MagicM.adjoint() * X * MagicM / norm_X; | |
331 | ||||
332 | // Find a common eigendecomposition of Re( X'.adjoint * X' ) and Im( | |||
333 | // X'.adjoint * X' ) Use pseudorandom linear comb to avoid issues with | |||
334 | // multiplicities > 1 | |||
335 |
3/6✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3317 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3317 times.
✗ Branch 8 not taken.
|
3317 | Mat4 X2 = Xprime.transpose() * Xprime; | |
336 | // For Clifford matrix, SelfAdjointEigenSolver seems to have a higher chance | |||
337 | // to produce eigenvectors that eventually lead to non-clifford angles when | |||
338 | // there are rounding errors. In this case, the matrix X2 should consist of | |||
339 | // 0, 1, -1, i and -i entries only, so clamp to these values. | |||
340 |
1/2✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
|
3317 | X2 = X2.unaryExpr([](Complex x) -> Complex { | |
341 |
2/2✓ Branch 1 taken 22142 times.
✓ Branch 2 taken 30930 times.
|
2/2✓ Decision 'true' taken 22142 times.
✓ Decision 'false' taken 30930 times.
|
53072 | if (std::abs(x) < 1e-14) { |
342 | 22142 | return 0.; | ||
343 |
2/2✓ Branch 2 taken 405 times.
✓ Branch 3 taken 30525 times.
|
2/2✓ Decision 'true' taken 405 times.
✓ Decision 'false' taken 30525 times.
|
30930 | } else if (std::abs(x - 1.) < 1e-14) { |
344 | 405 | return 1.; | ||
345 |
2/2✓ Branch 2 taken 399 times.
✓ Branch 3 taken 30126 times.
|
2/2✓ Decision 'true' taken 399 times.
✓ Decision 'false' taken 30126 times.
|
30525 | } else if (std::abs(x + 1.) < 1e-14) { |
346 | 399 | return -1.; | ||
347 |
2/2✓ Branch 2 taken 3568 times.
✓ Branch 3 taken 26558 times.
|
2/2✓ Decision 'true' taken 3568 times.
✓ Decision 'false' taken 26558 times.
|
30126 | } else if (std::abs(x - i_) < 1e-14) { |
348 | 3568 | return i_; | ||
349 |
2/2✓ Branch 2 taken 652 times.
✓ Branch 3 taken 25906 times.
|
2/2✓ Decision 'true' taken 652 times.
✓ Decision 'false' taken 25906 times.
|
26558 | } else if (std::abs(x + i_) < 1e-14) { |
350 | 652 | return -i_; | ||
351 | } else { | |||
352 | 25906 | return x; | ||
353 | } | |||
354 |
1/2✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
|
3317 | }); | |
355 |
2/4✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3317 times.
✗ Branch 5 not taken.
|
3317 | const Eigen::Matrix4d X2real = X2.real(); | |
356 |
2/4✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3317 times.
✗ Branch 5 not taken.
|
3317 | const Eigen::Matrix4d X2imag = X2.imag(); | |
357 |
1/2✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
|
3317 | Mat4 eigv; | |
358 |
1/2✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
|
3317 | Vec4 eigs; | |
359 | 3317 | double r = 0.; | ||
360 | while (true) { | |||
361 | 3317 | r += 1 / PI; | ||
362 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3317 times.
|
1/2✓ Decision 'true' taken 3317 times.
✗ Decision 'false' not taken.
|
3317 | if (r >= 1) r -= 1; |
363 | Eigen::SelfAdjointEigenSolver<Eigen::Matrix4d> ces( | |||
364 |
4/8✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3317 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3317 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3317 times.
✗ Branch 11 not taken.
|
3317 | r * X2real + (1 - r) * X2imag); | |
365 |
2/4✓ Branch 2 taken 3317 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3317 times.
✗ Branch 6 not taken.
|
3317 | eigv = ces.eigenvectors().cast<Complex>(); | |
366 |
5/10✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3317 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3317 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3317 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 3317 times.
✗ Branch 14 not taken.
|
3317 | eigs = (eigv.transpose() * X2 * eigv).diagonal(); | |
367 | ||||
368 |
7/14✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3317 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3317 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3317 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 3317 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 3317 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 3317 times.
✗ Branch 20 not taken.
|
3317 | if (std::abs((X2 - eigv * eigs.asDiagonal() * eigv.adjoint()).sum()) < | |
369 | EPS) { | |||
370 | // success! | |||
371 | 3317 | break; | ||
372 | } | |||
373 | } | |||
374 | ||||
375 | Eigen::Vector4d thetas = | |||
376 |
1/2✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
|
16585 | eigs.unaryExpr([](const Complex lambda) { return std::arg(lambda) / 2.; }) | |
377 |
2/4✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3317 times.
✗ Branch 5 not taken.
|
3317 | .real(); | |
378 | // make sure exp(i thetas) is in SU(4) ie Σ thetas = 0 | |||
379 |
3/6✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3317 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3317 times.
✗ Branch 8 not taken.
|
3317 | thetas(3) = -thetas.head<3>().sum(); | |
380 | ||||
381 |
3/6✓ Branch 2 taken 3317 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3317 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 3317 times.
✗ Branch 9 not taken.
|
3317 | const Mat4 tmp = (-i_ * thetas).asDiagonal(); | |
382 |
1/2✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
|
3317 | const auto eigs_sqrt_inv = tmp.exp(); | |
383 | ||||
384 |
2/4✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3317 times.
✗ Branch 5 not taken.
|
3317 | Mat4 Q2 = eigv.transpose(); | |
385 |
4/8✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3317 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3317 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3317 times.
✗ Branch 11 not taken.
|
3317 | Mat4 Q1 = Xprime * Q2.transpose() * eigs_sqrt_inv; | |
386 | ||||
387 | // we now have Xprime = Q1 * exp(i thetas) * Q2 | |||
388 | // with Q1,Q2 in SO(4), exp(i thetas) in SU(4) | |||
389 | ||||
390 | // transform to Pauli basis exp(i k_i σ_ii) | |||
391 |
1/2✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
|
3317 | Eigen::Matrix4d basis_change; | |
392 |
15/30✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3317 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3317 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3317 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 3317 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 3317 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 3317 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 3317 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 3317 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 3317 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 3317 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 3317 times.
✗ Branch 35 not taken.
✓ Branch 37 taken 3317 times.
✗ Branch 38 not taken.
✓ Branch 40 taken 3317 times.
✗ Branch 41 not taken.
✓ Branch 43 taken 3317 times.
✗ Branch 44 not taken.
|
3317 | basis_change << 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, | |
393 |
1/2✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
|
3317 | 1; // k3 = 0 always | |
394 | Eigen::Vector3d k = | |||
395 |
5/10✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3317 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3317 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3317 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 3317 times.
✗ Branch 14 not taken.
|
3317 | (-.5 * basis_change * thetas / PI).head<3>().unaryExpr([](double d) { | |
396 | 9951 | return mod(d, 4); | ||
397 |
1/2✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
|
3317 | }); | |
398 | ||||
399 | // we need to ensure that det(Q) == 1 so that K1,K2 \in SU(2) x SU(2) | |||
400 |
3/4✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2010 times.
✓ Branch 5 taken 1307 times.
|
3317 | if (Q2.determinant().real() < 0) { | |
401 |
4/8✓ Branch 1 taken 2010 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2010 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2010 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2010 times.
✗ Branch 11 not taken.
|
2010 | Q2.row(3) = -Q2.row(3); | |
402 |
4/8✓ Branch 1 taken 2010 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2010 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2010 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2010 times.
✗ Branch 11 not taken.
|
2010 | Q1 = Xprime * Q2.transpose() * eigs_sqrt_inv; | |
403 | } | |||
404 | ||||
405 | // these are our local operations (left and right) | |||
406 |
4/8✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3317 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3317 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3317 times.
✗ Branch 11 not taken.
|
3317 | Mat4 K1 = MagicM * Q1 * MagicM.adjoint(); | |
407 |
4/8✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3317 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3317 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3317 times.
✗ Branch 11 not taken.
|
3317 | Mat4 K2 = MagicM * Q2 * MagicM.adjoint(); | |
408 | ||||
409 | // fix phase | |||
410 |
1/2✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
|
3317 | K1 *= norm_X; | |
411 | ||||
412 | // Finally, we got our ks | |||
413 |
3/6✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3317 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3317 times.
✗ Branch 8 not taken.
|
3317 | ExpGate A{k(0), k(1), k(2)}; | |
414 | ||||
415 | // K1,K2 in SU(2)xSU(2), A = Exp(i aσ_XX + i bσ_YY + i cσ_ZZ) | |||
416 |
1/2✓ Branch 1 taken 3317 times.
✗ Branch 2 not taken.
|
6634 | return std::tuple<Mat4, ExpGate, Mat4>{K1, A, K2}; | |
417 | } | |||
418 | ||||
419 | 6404 | std::pair<Eigen::Matrix2cd, Eigen::Matrix2cd> kronecker_decomposition( | ||
420 | Eigen::Matrix4cd &U) { | |||
421 | using Mat4 = Eigen::Matrix4cd; | |||
422 |
4/8✓ Branch 1 taken 6404 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6404 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6404 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 6404 times.
✗ Branch 11 not taken.
|
6404 | Mat4 Up = U / pow(U.determinant(), 0.25); | |
423 |
1/2✓ Branch 1 taken 6404 times.
✗ Branch 2 not taken.
|
6404 | Mat4 U_mod; | |
424 |
14/28✓ Branch 1 taken 6404 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6404 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6404 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 6404 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 6404 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 6404 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 6404 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 6404 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 6404 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 6404 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 6404 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 6404 times.
✗ Branch 35 not taken.
✓ Branch 37 taken 6404 times.
✗ Branch 38 not taken.
✓ Branch 40 taken 6404 times.
✗ Branch 41 not taken.
|
12808 | U_mod << Up(0, 0), Up(1, 0), Up(0, 1), Up(1, 1), Up(2, 0), Up(3, 0), Up(2, 1), | |
425 |
14/28✓ Branch 1 taken 6404 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6404 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6404 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 6404 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 6404 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 6404 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 6404 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 6404 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 6404 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 6404 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 6404 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 6404 times.
✗ Branch 35 not taken.
✓ Branch 37 taken 6404 times.
✗ Branch 38 not taken.
✓ Branch 40 taken 6404 times.
✗ Branch 41 not taken.
|
6404 | Up(3, 1), Up(0, 2), Up(1, 2), Up(0, 3), Up(1, 3), Up(2, 2), Up(3, 2), | |
426 |
4/8✓ Branch 1 taken 6404 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6404 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6404 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 6404 times.
✗ Branch 11 not taken.
|
6404 | Up(2, 3), Up(3, 3); | |
427 | Eigen::JacobiSVD<Mat4, Eigen::NoQRPreconditioner> svd( | |||
428 | U_mod, Eigen::DecompositionOptions::ComputeFullU | | |||
429 |
1/2✓ Branch 1 taken 6404 times.
✗ Branch 2 not taken.
|
6404 | Eigen::DecompositionOptions::ComputeFullV); | |
430 |
2/4✓ Branch 1 taken 6404 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6404 times.
✗ Branch 5 not taken.
|
6404 | Eigen::Vector4cd sings = svd.singularValues(); | |
431 |
2/4✓ Branch 1 taken 6404 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6404 times.
✗ Branch 5 not taken.
|
6404 | Mat4 l = svd.matrixU(); | |
432 |
3/6✓ Branch 1 taken 6404 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6404 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6404 times.
✗ Branch 8 not taken.
|
6404 | Mat4 r = svd.matrixV().conjugate(); | |
433 |
2/4✓ Branch 1 taken 6404 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6404 times.
✗ Branch 5 not taken.
|
6404 | Eigen::Matrix2cd u0, u1; | |
434 |
8/16✓ Branch 1 taken 6404 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6404 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6404 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 6404 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 6404 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 6404 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 6404 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 6404 times.
✗ Branch 23 not taken.
|
6404 | u0 << l(0, 0), l(2, 0), l(1, 0), l(3, 0); | |
435 |
8/16✓ Branch 1 taken 6404 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6404 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6404 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 6404 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 6404 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 6404 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 6404 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 6404 times.
✗ Branch 23 not taken.
|
6404 | u1 << r(0, 0), r(2, 0), r(1, 0), r(3, 0); | |
436 |
2/4✓ Branch 1 taken 6404 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 6404 times.
✗ Branch 6 not taken.
|
6404 | u0 *= sqrt(sings[0]); | |
437 |
2/4✓ Branch 1 taken 6404 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 6404 times.
✗ Branch 6 not taken.
|
6404 | u1 *= sqrt(sings[0]); | |
438 |
1/2✓ Branch 1 taken 6404 times.
✗ Branch 2 not taken.
|
12808 | return {u0, u1}; | |
439 | 6404 | } | ||
440 | ||||
441 | 600451 | unsigned get_matrix_size(unsigned number_of_qubits) { | ||
442 | // Left-shifting by the number of bits stored by the int type, | |||
443 | // or more, is UNDEFINED by the C++ standard! | |||
444 | // It does NOT necessarily give zero! | |||
445 |
2/2✓ Branch 0 taken 599450 times.
✓ Branch 1 taken 1001 times.
|
600451 | if (number_of_qubits < std::numeric_limits<unsigned>::digits) { | |
446 | 599450 | unsigned size = 1; | ||
447 | 599450 | size <<= number_of_qubits; | ||
448 | 599450 | return size; | ||
449 | } | |||
450 |
1/2✓ Branch 1 taken 1001 times.
✗ Branch 2 not taken.
|
1001 | std::stringstream ss; | |
451 |
3/6✓ Branch 1 taken 1001 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1001 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1001 times.
✗ Branch 8 not taken.
|
1001 | ss << "get_matrix_size for " << number_of_qubits << " qubits; overflow!"; | |
452 |
2/4✓ Branch 2 taken 1001 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1001 times.
✗ Branch 6 not taken.
|
1001 | throw std::runtime_error(ss.str()); | |
453 | 1001 | } | ||
454 | ||||
455 | 204569 | unsigned get_number_of_qubits(unsigned matrix_size) { | ||
456 | 204569 | unsigned n_qubits = (unsigned)log2(matrix_size); | ||
457 |
3/4✓ Branch 1 taken 204569 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 201347 times.
✓ Branch 4 taken 3222 times.
|
204569 | if (get_matrix_size(n_qubits) == matrix_size) { | |
458 | 201347 | return n_qubits; | ||
459 | } | |||
460 |
1/2✓ Branch 1 taken 3222 times.
✗ Branch 2 not taken.
|
3222 | std::stringstream ss; | |
461 |
2/4✓ Branch 1 taken 3222 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3222 times.
✗ Branch 5 not taken.
|
3222 | ss << "get_number_of_qubits: matrix size " << matrix_size | |
462 |
1/2✓ Branch 1 taken 3222 times.
✗ Branch 2 not taken.
|
3222 | << " is not a power of two"; | |
463 |
2/4✓ Branch 2 taken 3222 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3222 times.
✗ Branch 6 not taken.
|
3222 | throw std::runtime_error(ss.str()); | |
464 | 3222 | } | ||
465 | ||||
466 | 193336 | SparseMatrixXcd get_sparse_matrix( | ||
467 | const std::vector<TripletCd> &triplets, unsigned rows, unsigned cols) { | |||
468 | 193336 | SparseMatrixXcd matr(rows, cols); | ||
469 |
1/2✓ Branch 3 taken 193336 times.
✗ Branch 4 not taken.
|
193336 | matr.setFromTriplets(triplets.cbegin(), triplets.cend()); | |
470 | 193336 | return matr; | ||
471 | } | |||
472 | ||||
473 | 192750 | SparseMatrixXcd get_sparse_square_matrix( | ||
474 | const std::vector<TripletCd> &triplets, unsigned rows) { | |||
475 | 192750 | return get_sparse_matrix(triplets, rows, rows); | ||
476 | } | |||
477 | ||||
478 | ✗ | std::vector<TripletCd> get_triplets( | ||
479 | const SparseMatrixXcd &matr, double abs_epsilon) { | |||
480 | ✗ | std::vector<TripletCd> result; | ||
481 | ✗ | for (unsigned kk = 0; kk < matr.outerSize(); ++kk) { | ||
482 | ✗ | for (SparseMatrixXcd::InnerIterator it(matr, kk); it; ++it) { | ||
483 | ✗ | if (std::abs(it.value()) > abs_epsilon) { | ||
484 | ✗ | const unsigned row = it.row(); | ||
485 | ✗ | const unsigned col = it.col(); | ||
486 | ✗ | result.emplace_back(row, col, it.value()); | ||
487 | } | |||
488 | } | |||
489 | } | |||
490 | ✗ | return result; | ||
491 | } | |||
492 | ||||
493 | 191431 | std::vector<TripletCd> get_triplets( | ||
494 | const Eigen::MatrixXcd &matr, double abs_epsilon) { | |||
495 | 191431 | std::vector<TripletCd> triplets; | ||
496 |
2/2✓ Branch 1 taken 550257 times.
✓ Branch 2 taken 191431 times.
|
741688 | for (unsigned jj = 0; jj < matr.cols(); ++jj) { | |
497 |
2/2✓ Branch 1 taken 1781185 times.
✓ Branch 2 taken 550257 times.
|
2331442 | for (unsigned ii = 0; ii < matr.rows(); ++ii) { | |
498 |
3/4✓ Branch 1 taken 1781185 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 702533 times.
✓ Branch 5 taken 1078652 times.
|
1781185 | if (!(std::abs(matr(ii, jj)) <= abs_epsilon)) { | |
499 |
2/4✓ Branch 1 taken 702533 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 702533 times.
✗ Branch 5 not taken.
|
702533 | triplets.emplace_back(ii, jj, matr(ii, jj)); | |
500 | } | |||
501 | } | |||
502 | } | |||
503 | 191431 | return triplets; | ||
504 | } | |||
505 | ||||
506 | 261 | bool in_weyl_chamber(const std::array<Expr, 3> &k) { | ||
507 | 261 | bool is_symbolic = true; | ||
508 | 261 | double last_val = .5; | ||
509 |
2/2✓ Branch 1 taken 768 times.
✓ Branch 2 taken 246 times.
|
1014 | for (unsigned i = 0; i < k.size(); ++i) { | |
510 |
1/2✓ Branch 2 taken 768 times.
✗ Branch 3 not taken.
|
768 | std::optional<double> eval = eval_expr_mod(k[i], 4); | |
511 |
2/2✓ Branch 1 taken 728 times.
✓ Branch 2 taken 40 times.
|
768 | if (eval) { | |
512 | 728 | is_symbolic = false; | ||
513 |
2/2✓ Branch 1 taken 244 times.
✓ Branch 2 taken 484 times.
|
728 | if (i + 1 == k.size()) { | |
514 | 244 | double abs_eval = std::min(*eval, -(*eval) + 4); | ||
515 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 241 times.
|
244 | if (abs_eval - last_val > EPS) { | |
516 | 15 | return false; | ||
517 | } | |||
518 | } else { | |||
519 |
2/2✓ Branch 1 taken 11 times.
✓ Branch 2 taken 473 times.
|
484 | if (*eval - last_val > EPS) { | |
520 | 11 | return false; | ||
521 | } | |||
522 | } | |||
523 | 714 | last_val = *eval; | ||
524 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 39 times.
|
40 | } else if (!is_symbolic) { | |
525 | 1 | return false; | ||
526 | } | |||
527 | } | |||
528 | 246 | return true; | ||
529 | } | |||
530 | ||||
531 | 3096 | Eigen::Matrix2cd nth_root(const Eigen::Matrix2cd &u, unsigned n) { | ||
532 |
4/6✓ Branch 1 taken 3096 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3096 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 3094 times.
|
3096 | if (u.isApprox(Eigen::Matrix2cd::Identity(), EPS)) { | |
533 |
2/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
4 | return Eigen::Matrix2cd::Identity(); | |
534 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3093 times.
|
3094 | } else if (n == 0) { | |
535 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | throw std::invalid_argument("Non-identity matrix does not have a 0th root"); | |
536 | } | |||
537 |
1/2✓ Branch 1 taken 3093 times.
✗ Branch 2 not taken.
|
3093 | Eigen::ComplexEigenSolver<Eigen::Matrix2cd> eigen_solver(u); | |
538 |
3/6✓ Branch 2 taken 3093 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3093 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 3093 times.
✗ Branch 9 not taken.
|
3093 | return std::pow(eigen_solver.eigenvalues()[0], 1. / n) * | |
539 |
2/4✓ Branch 2 taken 3093 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3093 times.
✗ Branch 6 not taken.
|
6186 | eigen_solver.eigenvectors().col(0) * | |
540 |
3/6✓ Branch 2 taken 3093 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3093 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 3093 times.
✗ Branch 9 not taken.
|
6186 | eigen_solver.eigenvectors().col(0).adjoint() + | |
541 |
3/6✓ Branch 2 taken 3093 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3093 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 3093 times.
✗ Branch 9 not taken.
|
3093 | std::pow(eigen_solver.eigenvalues()[1], 1. / n) * | |
542 |
2/4✓ Branch 2 taken 3093 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3093 times.
✗ Branch 6 not taken.
|
6186 | eigen_solver.eigenvectors().col(1) * | |
543 |
3/6✓ Branch 2 taken 3093 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3093 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 3093 times.
✗ Branch 9 not taken.
|
9279 | eigen_solver.eigenvectors().col(1).adjoint(); | |
544 | } | |||
545 | ||||
546 | } // namespace tket | |||
547 |