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 |