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