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 "PQPSquash.hpp" | |||
16 | ||||
17 | #include <memory> | |||
18 | ||||
19 | #include "BasicOptimisation.hpp" | |||
20 | #include "Circuit/DAGDefs.hpp" | |||
21 | #include "Decomposition.hpp" | |||
22 | #include "Gate/Rotation.hpp" | |||
23 | #include "OpType/OpTypeInfo.hpp" | |||
24 | #include "Transform.hpp" | |||
25 | #include "Utils/Expression.hpp" | |||
26 | ||||
27 | namespace tket { | |||
28 | ||||
29 | namespace Transforms { | |||
30 | ||||
31 | static bool fixup_angles( | |||
32 | Expr &angle_p1, Expr &angle_q, Expr &angle_p2, bool reversed = false); | |||
33 | static bool redundancy_removal(Circuit &circ); | |||
34 | static Rotation merge_rotations( | |||
35 | OpType r, const std::vector<Gate_ptr> &chain, | |||
36 | std::vector<Gate_ptr>::const_iterator &iter); | |||
37 | ||||
38 | 6357 | PQPSquasher::PQPSquasher(OpType p, OpType q, bool smart_squash, bool reversed) | ||
39 | 6357 | : p_(p), | ||
40 | 6357 | q_(q), | ||
41 | 6357 | smart_squash_(smart_squash), | ||
42 | 6357 | reversed_(reversed), | ||
43 | 6357 | rotation_chain() { | ||
44 |
7/8✓ Branch 0 taken 6289 times.
✓ Branch 1 taken 68 times.
✓ Branch 2 taken 6283 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 6283 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3167 times.
✓ Branch 7 taken 3190 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 6357 times.
|
6357 | if (!(p == OpType::Rx || p == OpType::Ry || p == OpType::Rz) || |
45 |
3/4✓ Branch 0 taken 69 times.
✓ Branch 1 taken 3098 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 69 times.
|
3167 | !(q == OpType::Rx || q == OpType::Ry || q == OpType::Rz)) { | |
46 | ✗ | throw std::logic_error("Can only reduce chains of single qubit rotations"); | ||
47 | } | |||
48 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6357 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 6357 times.
|
6357 | if (p == q) { |
49 | ✗ | throw std::logic_error( | ||
50 | "Requires two different bases to perform single qubit " | |||
51 | ✗ | "rotations"); | ||
52 | } | |||
53 | 6357 | } | ||
54 | ||||
55 | 403984 | bool PQPSquasher::accepts(Gate_ptr gp) const { | ||
56 | 403984 | OpType type = gp->get_type(); | ||
57 |
4/4✓ Branch 0 taken 122396 times.
✓ Branch 1 taken 281588 times.
✓ Branch 2 taken 122140 times.
✓ Branch 3 taken 256 times.
|
403984 | return type == p_ || type == q_; | |
58 | } | |||
59 | ||||
60 | 201862 | void PQPSquasher::append(Gate_ptr gp) { | ||
61 |
2/4✓ Branch 2 taken 201862 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 201862 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 201862 times.
|
201862 | if (!accepts(gp)) { |
62 | ✗ | throw BadOpType("PQPSquasher: cannot append OpType", gp->get_type()); | ||
63 | } | |||
64 | 201862 | rotation_chain.push_back(gp); | ||
65 | 201862 | } | ||
66 | ||||
67 | 37101 | std::pair<Circuit, Gate_ptr> PQPSquasher::flush( | ||
68 | std::optional<Pauli> commutation_colour) const { | |||
69 | 37101 | bool commute_through = false; | ||
70 | 37101 | OpType p = p_, q = q_; | ||
71 | ||||
72 |
6/6✓ Branch 0 taken 1076 times.
✓ Branch 1 taken 36025 times.
✓ Branch 3 taken 351 times.
✓ Branch 4 taken 725 times.
✓ Branch 5 taken 351 times.
✓ Branch 6 taken 36750 times.
|
2/2✓ Decision 'true' taken 1053 times.
✓ Decision 'false' taken 36048 times.
|
37101 | if (smart_squash_ && commutation_colour.has_value()) { |
73 | // Using an arbitrary non-zero angle to obtain the commutation for p_/q_. | |||
74 |
3/6✓ Branch 1 taken 351 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 351 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 351 times.
✗ Branch 9 not taken.
|
1053 | Gate P(p_, {0.123}, 1); | |
75 |
3/6✓ Branch 1 taken 351 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 351 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 351 times.
✗ Branch 9 not taken.
|
1053 | Gate Q(q_, {0.123}, 1); | |
76 |
3/4✓ Branch 1 taken 351 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 161 times.
✓ Branch 4 taken 190 times.
|
2/2✓ Decision 'true' taken 161 times.
✓ Decision 'false' taken 190 times.
|
351 | if (P.commutes_with_basis(commutation_colour, 0)) { |
77 | 161 | commute_through = true; | ||
78 |
3/4✓ Branch 1 taken 190 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 179 times.
✓ Branch 4 taken 11 times.
|
2/2✓ Decision 'true' taken 179 times.
✓ Decision 'false' taken 11 times.
|
190 | } else if (Q.commutes_with_basis(commutation_colour, 0)) { |
79 | 179 | commute_through = true; | ||
80 | 179 | p = q_; | ||
81 | 179 | q = p_; | ||
82 | } | |||
83 | 351 | } | ||
84 | ||||
85 | // Construct list of merged rotations | |||
86 | 37101 | std::list<Rotation> rots; | ||
87 | 37101 | auto iter = rotation_chain.cbegin(); | ||
88 |
2/2✓ Branch 2 taken 93917 times.
✓ Branch 3 taken 37101 times.
|
2/2✓ Decision 'true' taken 93917 times.
✓ Decision 'false' taken 37101 times.
|
131018 | while (iter != rotation_chain.cend()) { |
89 | // Merge next q rotations | |||
90 |
2/4✓ Branch 1 taken 93917 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 93917 times.
✗ Branch 5 not taken.
|
93917 | rots.push_back(merge_rotations(q, rotation_chain, iter)); | |
91 | // Merge next p rotations | |||
92 |
2/4✓ Branch 1 taken 93917 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 93917 times.
✗ Branch 5 not taken.
|
93917 | rots.push_back(merge_rotations(p, rotation_chain, iter)); | |
93 | } | |||
94 | ||||
95 | // Perform any cancellations | |||
96 | 37101 | std::list<Rotation>::iterator r = rots.begin(); | ||
97 |
2/2✓ Branch 2 taken 187834 times.
✓ Branch 3 taken 37101 times.
|
2/2✓ Decision 'true' taken 187834 times.
✓ Decision 'false' taken 37101 times.
|
224935 | while (r != rots.end()) { |
98 |
2/2✓ Branch 2 taken 84132 times.
✓ Branch 3 taken 103702 times.
|
2/2✓ Decision 'true' taken 84132 times.
✓ Decision 'false' taken 103702 times.
|
187834 | if (r->is_id()) { |
99 | 84132 | r = rots.erase(r); | ||
100 |
6/6✓ Branch 2 taken 19838 times.
✓ Branch 3 taken 64294 times.
✓ Branch 6 taken 13917 times.
✓ Branch 7 taken 5921 times.
✓ Branch 8 taken 13917 times.
✓ Branch 9 taken 70215 times.
|
2/2✓ Decision 'true' taken 13917 times.
✓ Decision 'false' taken 70215 times.
|
84132 | if (r != rots.begin() && r != rots.end()) { |
101 |
2/4✓ Branch 1 taken 13917 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 13917 times.
✗ Branch 7 not taken.
|
13917 | std::prev(r)->apply(*r); | |
102 | 13917 | r = rots.erase(r); | ||
103 | 13917 | r--; | ||
104 | } | |||
105 | } else | |||
106 | 103702 | r++; | ||
107 | } | |||
108 | ||||
109 | // Extract any P rotations from the beginning and end of the list | |||
110 |
2/4✓ Branch 1 taken 37101 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 37101 times.
✗ Branch 5 not taken.
|
37101 | Expr p1 = 0, p2 = 0; | |
111 | 37101 | std::list<Rotation>::iterator i1 = rots.begin(); | ||
112 |
2/2✓ Branch 2 taken 37055 times.
✓ Branch 3 taken 46 times.
|
2/2✓ Decision 'true' taken 37055 times.
✓ Decision 'false' taken 46 times.
|
37101 | if (i1 != rots.end()) { |
113 |
1/2✓ Branch 2 taken 37055 times.
✗ Branch 3 not taken.
|
37055 | std::optional<Expr> a = i1->angle(p); | |
114 |
2/2✓ Branch 1 taken 28061 times.
✓ Branch 2 taken 8994 times.
|
2/2✓ Decision 'true' taken 28061 times.
✓ Decision 'false' taken 8994 times.
|
37055 | if (a) { |
115 |
2/4✓ Branch 1 taken 28061 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 28061 times.
✗ Branch 5 not taken.
|
28061 | p1 = a.value(); | |
116 | 28061 | rots.pop_front(); | ||
117 | } | |||
118 | 37055 | } | ||
119 | 37101 | std::list<Rotation>::reverse_iterator i2 = rots.rbegin(); | ||
120 |
3/4✓ Branch 2 taken 37101 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 27349 times.
✓ Branch 5 taken 9752 times.
|
2/2✓ Decision 'true' taken 27349 times.
✓ Decision 'false' taken 9752 times.
|
37101 | if (i2 != rots.rend()) { |
121 |
2/4✓ Branch 1 taken 27349 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 27349 times.
✗ Branch 5 not taken.
|
27349 | std::optional<Expr> a = i2->angle(p); | |
122 |
2/2✓ Branch 1 taken 21459 times.
✓ Branch 2 taken 5890 times.
|
2/2✓ Decision 'true' taken 21459 times.
✓ Decision 'false' taken 5890 times.
|
27349 | if (a) { |
123 |
2/4✓ Branch 1 taken 21459 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 21459 times.
✗ Branch 5 not taken.
|
21459 | p2 = a.value(); | |
124 | 21459 | rots.pop_back(); | ||
125 | } | |||
126 | 27349 | } | ||
127 | ||||
128 | // Finish up: | |||
129 |
1/2✓ Branch 1 taken 37101 times.
✗ Branch 2 not taken.
|
37101 | Rotation R = {}; | |
130 |
3/4✓ Branch 4 taken 40265 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 40265 times.
✓ Branch 9 taken 37101 times.
|
0/1? Decision couldn't be analyzed.
|
77366 | for (auto rot : rots) { |
131 |
1/2✓ Branch 1 taken 40265 times.
✗ Branch 2 not taken.
|
40265 | R.apply(rot); | |
132 | 40265 | } | ||
133 |
1/2✓ Branch 1 taken 37101 times.
✗ Branch 2 not taken.
|
37101 | std::tuple<Expr, Expr, Expr> angles = R.to_pqp(p, q); | |
134 | ||||
135 |
1/2✓ Branch 2 taken 37101 times.
✗ Branch 3 not taken.
|
37101 | Expr angle_p1 = std::get<0>(angles) + p1; | |
136 |
1/2✓ Branch 2 taken 37101 times.
✗ Branch 3 not taken.
|
37101 | Expr angle_q = std::get<1>(angles); | |
137 |
1/2✓ Branch 2 taken 37101 times.
✗ Branch 3 not taken.
|
37101 | Expr angle_p2 = std::get<2>(angles) + p2; | |
138 |
1/2✓ Branch 1 taken 37101 times.
✗ Branch 2 not taken.
|
37101 | fixup_angles(angle_p1, angle_q, angle_p2, reversed_); | |
139 | ||||
140 |
1/2✓ Branch 2 taken 37101 times.
✗ Branch 3 not taken.
|
37101 | Circuit replacement(1); | |
141 | 37101 | Gate_ptr left_over_gate = nullptr; | ||
142 |
3/4✓ Branch 1 taken 37101 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 24660 times.
✓ Branch 4 taken 12441 times.
|
2/2✓ Decision 'true' taken 24660 times.
✓ Decision 'false' taken 12441 times.
|
37101 | if (!equiv_0(angle_p1, 4)) { |
143 |
10/12✓ Branch 1 taken 24660 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 9559 times.
✓ Branch 4 taken 15101 times.
✓ Branch 6 taken 9559 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 9321 times.
✓ Branch 9 taken 238 times.
✓ Branch 10 taken 55 times.
✓ Branch 11 taken 9266 times.
✓ Branch 12 taken 55 times.
✓ Branch 13 taken 24605 times.
|
2/2✓ Decision 'true' taken 110 times.
✓ Decision 'false' taken 24550 times.
|
24660 | if (equiv_0(angle_q, 4) && equiv_0(angle_p2, 4) && commute_through) { |
144 | left_over_gate = | |||
145 |
5/10✓ Branch 1 taken 55 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 55 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 55 times.
✗ Branch 9 not taken.
✓ Branch 14 taken 55 times.
✓ Branch 15 taken 55 times.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
|
110 | std::make_shared<Gate>(p, std::vector<Expr>{angle_p1}, 1); | |
146 | } else { | |||
147 |
2/4✓ Branch 3 taken 24605 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 24605 times.
✗ Branch 7 not taken.
|
24605 | replacement.add_op<unsigned>(p, angle_p1, {0}); | |
148 | } | |||
149 | } | |||
150 |
3/4✓ Branch 1 taken 37101 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 26909 times.
✓ Branch 4 taken 10192 times.
|
2/2✓ Decision 'true' taken 26909 times.
✓ Decision 'false' taken 10192 times.
|
37101 | if (!equiv_0(angle_q, 4)) { |
151 |
2/4✓ Branch 3 taken 26909 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 26909 times.
✗ Branch 7 not taken.
|
26909 | replacement.add_op<unsigned>(q, angle_q, {0}); | |
152 | } | |||
153 |
3/4✓ Branch 1 taken 37101 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 20724 times.
✓ Branch 4 taken 16377 times.
|
2/2✓ Decision 'true' taken 20724 times.
✓ Decision 'false' taken 16377 times.
|
37101 | if (!equiv_0(angle_p2, 4)) { |
154 |
2/2✓ Branch 0 taken 86 times.
✓ Branch 1 taken 20638 times.
|
2/2✓ Decision 'true' taken 172 times.
✓ Decision 'false' taken 20552 times.
|
20724 | if (commute_through) { |
155 | left_over_gate = | |||
156 |
5/10✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 86 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 86 times.
✗ Branch 9 not taken.
✓ Branch 14 taken 86 times.
✓ Branch 15 taken 86 times.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
|
172 | std::make_shared<Gate>(p, std::vector<Expr>{angle_p2}, 1); | |
157 | } else { | |||
158 |
2/4✓ Branch 3 taken 20638 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 20638 times.
✗ Branch 7 not taken.
|
20638 | replacement.add_op<unsigned>(p, angle_p2, {0}); | |
159 | } | |||
160 | } | |||
161 |
1/2✓ Branch 1 taken 37101 times.
✗ Branch 2 not taken.
|
37101 | redundancy_removal(replacement); | |
162 |
1/2✓ Branch 1 taken 37101 times.
✗ Branch 2 not taken.
|
74202 | return {replacement, left_over_gate}; | |
163 | 37101 | } | ||
164 | ||||
165 | 63459 | void PQPSquasher::clear() { rotation_chain.clear(); } | ||
166 | ||||
167 | ✗ | std::unique_ptr<AbstractSquasher> PQPSquasher::clone() const { | ||
168 | ✗ | return std::make_unique<PQPSquasher>(*this); | ||
169 | } | |||
170 | ||||
171 | 187834 | static Rotation merge_rotations( | ||
172 | OpType r, const std::vector<Gate_ptr> &chain, | |||
173 | std::vector<Gate_ptr>::const_iterator &iter) { | |||
174 |
1/2✓ Branch 1 taken 187834 times.
✗ Branch 2 not taken.
|
187834 | Expr total_angle(0); | |
175 |
2/2✓ Branch 2 taken 349912 times.
✓ Branch 3 taken 39784 times.
|
2/2✓ Decision 'true' taken 349912 times.
✓ Decision 'false' taken 39784 times.
|
389696 | while (iter != chain.end()) { |
176 | 349912 | const Gate_ptr rot_op = *iter; | ||
177 |
2/2✓ Branch 2 taken 148050 times.
✓ Branch 3 taken 201862 times.
|
2/2✓ Decision 'true' taken 148050 times.
✓ Decision 'false' taken 201862 times.
|
349912 | if (rot_op->get_type() != r) { |
178 | 148050 | break; | ||
179 | } | |||
180 |
2/4✓ Branch 2 taken 201862 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 201862 times.
✗ Branch 7 not taken.
|
201862 | total_angle += rot_op->get_params()[0]; | |
181 | 201862 | iter++; | ||
182 |
2/2✓ Branch 1 taken 201862 times.
✓ Branch 2 taken 148050 times.
|
349912 | } | |
183 |
2/4✓ Branch 1 taken 187834 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 187834 times.
✗ Branch 5 not taken.
|
375668 | return Rotation(r, total_angle); | |
184 | 187834 | } | ||
185 | ||||
186 | 6346 | static bool squash_to_pqp( | ||
187 | Circuit &circ, OpType q, OpType p, bool strict = false) { | |||
188 | 6346 | bool reverse = true; | ||
189 |
1/2✓ Branch 1 taken 6346 times.
✗ Branch 2 not taken.
|
6346 | auto squasher = std::make_unique<PQPSquasher>(p, q, !strict, reverse); | |
190 |
1/2✓ Branch 4 taken 6346 times.
✗ Branch 5 not taken.
|
12692 | return SingleQubitSquash(std::move(squasher), circ, reverse).squash(); | |
191 | 6346 | } | ||
192 | ||||
193 | 24 | Transform reduce_XZ_chains() { | ||
194 | 40 | return Transform([](Circuit &circ) { | ||
195 | 40 | return squash_to_pqp(circ, OpType::Rx, OpType::Rz); | ||
196 |
1/2✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
|
24 | }); | |
197 | } | |||
198 | ||||
199 | 6141 | Transform squash_1qb_to_pqp(const OpType &q, const OpType &p, bool strict) { | ||
200 | return Transform( | |||
201 |
1/2✓ Branch 2 taken 6141 times.
✗ Branch 3 not taken.
|
12447 | [=](Circuit &circ) { return squash_to_pqp(circ, q, p, strict); }); | |
202 | } | |||
203 | ||||
204 | // To squash to TK1: | |||
205 | // - we first decompose to ZYZ. Doing this was found to reduce the size of | |||
206 | // symbolic expressions | |||
207 | // - we then redecompose to ZXZ, so that we can commute Rz or Rx rotation past | |||
208 | // multi-qubit gates (most usual multi-qb gates commute with X or Z) | |||
209 | // - Rz and Rx rotations can then be straight-forwardly combined into TK1s. | |||
210 | 3020 | Transform squash_1qb_to_tk1() { | ||
211 |
1/2✓ Branch 2 taken 3020 times.
✗ Branch 3 not taken.
|
9060 | return Transforms::decompose_ZY() >> | |
212 |
2/4✓ Branch 1 taken 3020 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3020 times.
✗ Branch 5 not taken.
|
9060 | squash_1qb_to_pqp(OpType::Ry, OpType::Rz, true) >> | |
213 |
2/4✓ Branch 1 taken 3020 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3020 times.
✗ Branch 5 not taken.
|
9060 | Transforms::decompose_ZX() >> | |
214 |
1/2✓ Branch 1 taken 3020 times.
✗ Branch 2 not taken.
|
6040 | squash_1qb_to_pqp(OpType::Rx, OpType::Rz, true) >> | |
215 |
2/4✓ Branch 1 taken 3020 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3020 times.
✗ Branch 5 not taken.
|
9060 | Transforms::decompose_ZXZ_to_TK1(); | |
216 | } | |||
217 | ||||
218 | 37101 | static bool fixup_angles( | ||
219 | Expr &angle_p1, Expr &angle_q, Expr &angle_p2, bool reversed) { | |||
220 | 37101 | bool success = false; | ||
221 |
2/2✓ Branch 0 taken 37049 times.
✓ Branch 1 taken 52 times.
|
2/2✓ Decision 'true' taken 37049 times.
✓ Decision 'false' taken 52 times.
|
37101 | if (reversed) { |
222 | 37049 | std::swap(angle_p1, angle_p2); | ||
223 |
1/2✓ Branch 2 taken 37049 times.
✗ Branch 3 not taken.
|
37049 | angle_p1 *= -1; | |
224 |
1/2✓ Branch 2 taken 37049 times.
✗ Branch 3 not taken.
|
37049 | angle_q *= -1; | |
225 |
1/2✓ Branch 2 taken 37049 times.
✗ Branch 3 not taken.
|
37049 | angle_p2 *= -1; | |
226 | } | |||
227 |
6/6✓ Branch 1 taken 1864 times.
✓ Branch 2 taken 35237 times.
✓ Branch 4 taken 1689 times.
✓ Branch 5 taken 175 times.
✓ Branch 6 taken 1689 times.
✓ Branch 7 taken 35412 times.
|
2/2✓ Decision 'true' taken 1689 times.
✓ Decision 'false' taken 35412 times.
|
37101 | if (equiv_val(angle_q, 1., 2) && !equiv_0(angle_p2, 4)) { |
228 | // Prefer --P(p1-p2)--Q(...)--P(0)-- | |||
229 | // Only occurs if angle_q is pi or 3pi and angle_p2 is non-zero | |||
230 | 1689 | angle_p1 -= angle_p2; | ||
231 | 1689 | angle_p2 = 0; | ||
232 | 1689 | success = true; | ||
233 |
2/2✓ Branch 1 taken 1903 times.
✓ Branch 2 taken 33509 times.
|
2/2✓ Decision 'true' taken 1903 times.
✓ Decision 'false' taken 33509 times.
|
35412 | } else if (equiv_val(angle_p2, 1., 4)) { |
234 | // Then prefer --P(p1+p2)--Q(-q)--P(0)-- | |||
235 | // Only occurs if angle_p2 is pi | |||
236 |
1/2✓ Branch 2 taken 1903 times.
✗ Branch 3 not taken.
|
1903 | angle_p1 += 1; | |
237 |
1/2✓ Branch 2 taken 1903 times.
✗ Branch 3 not taken.
|
1903 | angle_q *= -1; | |
238 | 1903 | angle_p2 = 0; | ||
239 | 1903 | success = true; | ||
240 |
2/2✓ Branch 1 taken 562 times.
✓ Branch 2 taken 32947 times.
|
2/2✓ Decision 'true' taken 562 times.
✓ Decision 'false' taken 32947 times.
|
33509 | } else if (equiv_val(angle_p2, 3., 4)) { |
241 | // Then prefer --P(p1+p2)--Q(-q)--P(0)-- | |||
242 | // Only occurs if angle_p2 is 3pi | |||
243 |
1/2✓ Branch 2 taken 562 times.
✗ Branch 3 not taken.
|
562 | angle_p1 += 3; | |
244 |
1/2✓ Branch 2 taken 562 times.
✗ Branch 3 not taken.
|
562 | angle_q *= -1; | |
245 | 562 | angle_p2 = 0; | ||
246 | 562 | success = true; | ||
247 |
6/6✓ Branch 1 taken 3040 times.
✓ Branch 2 taken 29907 times.
✓ Branch 4 taken 1108 times.
✓ Branch 5 taken 1932 times.
✓ Branch 6 taken 1108 times.
✓ Branch 7 taken 31839 times.
|
2/2✓ Decision 'true' taken 1108 times.
✓ Decision 'false' taken 31839 times.
|
32947 | } else if (equiv_val(angle_p1, 1., 4) && !equiv_0(angle_p2, 4)) { |
248 | // Then prefer --P(0)--Q(-q)--P(p1+p2)-- | |||
249 | // Only occurs if angle_p1 is pi and angle_p2 is non-zero | |||
250 |
1/2✓ Branch 2 taken 1108 times.
✗ Branch 3 not taken.
|
1108 | angle_q *= -1; | |
251 |
1/2✓ Branch 2 taken 1108 times.
✗ Branch 3 not taken.
|
1108 | angle_p2 += 1; | |
252 | 1108 | angle_p1 = 0; | ||
253 | 1108 | success = true; | ||
254 |
6/6✓ Branch 1 taken 478 times.
✓ Branch 2 taken 31361 times.
✓ Branch 4 taken 253 times.
✓ Branch 5 taken 225 times.
✓ Branch 6 taken 253 times.
✓ Branch 7 taken 31586 times.
|
2/2✓ Decision 'true' taken 253 times.
✓ Decision 'false' taken 31586 times.
|
31839 | } else if (equiv_val(angle_p1, 3., 4) && !equiv_0(angle_p2, 4)) { |
255 | // Then prefer --P(0)--Q(-q)--P(p1+p2)-- | |||
256 | // Only occurs if angle_p1 is 3pi and angle_p2 is non-zero | |||
257 |
1/2✓ Branch 2 taken 253 times.
✗ Branch 3 not taken.
|
253 | angle_q *= -1; | |
258 |
1/2✓ Branch 2 taken 253 times.
✗ Branch 3 not taken.
|
253 | angle_p2 += 3; | |
259 | 253 | angle_p1 = 0; | ||
260 | 253 | success = true; | ||
261 | } | |||
262 |
2/2✓ Branch 0 taken 37049 times.
✓ Branch 1 taken 52 times.
|
2/2✓ Decision 'true' taken 37049 times.
✓ Decision 'false' taken 52 times.
|
37101 | if (reversed) { |
263 | 37049 | std::swap(angle_p1, angle_p2); | ||
264 |
1/2✓ Branch 2 taken 37049 times.
✗ Branch 3 not taken.
|
37049 | angle_p1 *= -1; | |
265 |
1/2✓ Branch 2 taken 37049 times.
✗ Branch 3 not taken.
|
37049 | angle_q *= -1; | |
266 |
1/2✓ Branch 2 taken 37049 times.
✗ Branch 3 not taken.
|
37049 | angle_p2 *= -1; | |
267 | } | |||
268 | 37101 | return success; | ||
269 | } | |||
270 | ||||
271 | 147290 | static bool remove_redundancy( | ||
272 | Circuit &circ, const Vertex &vert, VertexList &bin, | |||
273 | std::set<IVertex> &new_affected_verts, IndexMap &im) { | |||
274 |
1/2✓ Branch 1 taken 147290 times.
✗ Branch 2 not taken.
|
147290 | const Op_ptr op = circ.get_Op_ptr_from_Vertex(vert); | |
275 |
1/2✓ Branch 2 taken 147290 times.
✗ Branch 3 not taken.
|
147290 | const OpDesc desc = op->get_desc(); | |
276 |
3/4✓ Branch 1 taken 147290 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 74593 times.
✓ Branch 4 taken 72697 times.
|
2/2✓ Decision 'true' taken 72697 times.
✓ Decision 'false' taken 74593 times.
|
147290 | if (!desc.is_gate()) return false; |
277 |
7/10✓ Branch 1 taken 72697 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 72532 times.
✓ Branch 4 taken 165 times.
✓ Branch 6 taken 72532 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 72532 times.
✓ Branch 10 taken 165 times.
✓ Branch 11 taken 72532 times.
|
2/2✓ Decision 'true' taken 165 times.
✓ Decision 'false' taken 72532 times.
|
72697 | if (circ.n_out_edges(vert) == 0 || circ.n_in_edges(vert) == 0) { |
278 | 165 | return false; // either a boundary vert or we have already removed it | ||
279 | } | |||
280 | ||||
281 | 678 | auto remove_single_vertex = [&bin, &circ, &new_affected_verts, | ||
282 | 2712 | &im](const Vertex &v_remove) { | ||
283 | 678 | bin.push_back(v_remove); | ||
284 |
3/4✓ Branch 1 taken 678 times.
✗ Branch 2 not taken.
✓ Branch 7 taken 678 times.
✓ Branch 8 taken 678 times.
|
0/1? Decision couldn't be analyzed.
|
1356 | for (const Vertex &l : circ.get_predecessors(v_remove)) { |
285 |
2/4✓ Branch 1 taken 678 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 678 times.
✗ Branch 6 not taken.
|
678 | new_affected_verts.insert({im.at(l), l}); | |
286 | 678 | } | ||
287 | 678 | circ.remove_vertex( | ||
288 | v_remove, Circuit::GraphRewiring::Yes, Circuit::VertexDeletion::No); | |||
289 | 678 | }; | ||
290 | // remove 0 angle rotations from circuit | |||
291 |
1/2✓ Branch 2 taken 72532 times.
✗ Branch 3 not taken.
|
72532 | std::optional<double> a = op->is_identity(); | |
292 |
2/2✓ Branch 1 taken 678 times.
✓ Branch 2 taken 71854 times.
|
2/2✓ Decision 'true' taken 678 times.
✓ Decision 'false' taken 71854 times.
|
72532 | if (a) { |
293 |
1/2✓ Branch 1 taken 678 times.
✗ Branch 2 not taken.
|
678 | remove_single_vertex(vert); | |
294 |
3/6✓ Branch 1 taken 678 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 678 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 678 times.
✗ Branch 8 not taken.
|
678 | circ.add_phase(a.value()); | |
295 | 678 | return true; | ||
296 |
2/4✓ Branch 1 taken 71854 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 71854 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 71854 times.
|
71854 | } else if (desc.type() == OpType::noop) { |
297 | // remove "noop" gates from circuit | |||
298 | ✗ | remove_single_vertex(vert); | ||
299 | ✗ | return true; | ||
300 | } | |||
301 |
1/2✓ Branch 1 taken 71854 times.
✗ Branch 2 not taken.
|
71854 | VertexVec kids = circ.get_successors(vert); | |
302 | ||||
303 | // if op is immediately followed by a z basis measurement on all qubits, | |||
304 | // remove | |||
305 |
2/4✓ Branch 1 taken 71854 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 71854 times.
✗ Branch 4 not taken.
|
1/2✓ Decision 'true' taken 71854 times.
✗ Decision 'false' not taken.
|
71854 | if (circ.n_out_edges_of_type(vert, EdgeType::Classical) == 0) { |
306 | 71854 | bool z_followed_by_measures = true; | ||
307 |
5/6✓ Branch 1 taken 71854 times.
✓ Branch 2 taken 71854 times.
✓ Branch 3 taken 71854 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 71854 times.
✓ Branch 6 taken 71854 times.
|
0/1? Decision couldn't be analyzed.
|
143708 | for (port_t port = 0; port < kids.size() && z_followed_by_measures; |
308 | port++) { | |||
309 |
2/4✓ Branch 2 taken 71854 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 71854 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 71854 times.
|
71854 | if (circ.get_OpType_from_Vertex(kids[port]) == OpType::Measure) { |
310 | ✗ | z_followed_by_measures &= | ||
311 | ✗ | circ.commutes_with_basis(vert, Pauli::Z, PortType::Source, port); | ||
312 | } else { | |||
313 | 71854 | z_followed_by_measures = false; | ||
314 | } | |||
315 | } | |||
316 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 71854 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 71854 times.
|
71854 | if (z_followed_by_measures) { |
317 | ✗ | remove_single_vertex(vert); | ||
318 | ✗ | return true; | ||
319 | } | |||
320 | } | |||
321 | ||||
322 | // check that both the vertex and its successor have each other and only each | |||
323 | // other | |||
324 |
5/12✓ Branch 1 taken 71854 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 71854 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 71854 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 71854 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 71854 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
|
1/2✓ Decision 'true' taken 71854 times.
✗ Decision 'false' not taken.
|
71854 | if ((kids.size() == 1) && (circ.get_predecessors(kids[0]).size() == 1)) { |
325 | // check that the ports match up between vertices | |||
326 | 71854 | Vertex b = kids[0]; | ||
327 |
1/2✓ Branch 1 taken 71854 times.
✗ Branch 2 not taken.
|
71854 | EdgeVec ins = circ.get_in_edges(b); | |
328 |
2/2✓ Branch 5 taken 71854 times.
✓ Branch 6 taken 71854 times.
|
2/2✓ Decision 'true' taken 71854 times.
✓ Decision 'false' taken 71854 times.
|
143708 | for (const Edge &in : ins) { |
329 |
3/6✓ Branch 1 taken 71854 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 71854 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 71854 times.
|
1/2✓ Decision 'true' taken 71854 times.
✗ Decision 'false' not taken.
|
71854 | if (circ.get_source_port(in) != circ.get_target_port(in)) return false; |
330 | } | |||
331 | ||||
332 | // check that the classical edges match up correctly | |||
333 |
2/4✓ Branch 1 taken 71854 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 71854 times.
|
1/2✓ Decision 'true' taken 71854 times.
✗ Decision 'false' not taken.
|
71854 | if (circ.n_in_edges_of_type(vert, EdgeType::Boolean) != 0) return false; |
334 | ||||
335 |
1/2✓ Branch 1 taken 71854 times.
✗ Branch 2 not taken.
|
71854 | const Op_ptr b_op = circ.get_Op_ptr_from_Vertex(b); | |
336 |
1/2✓ Branch 2 taken 71854 times.
✗ Branch 3 not taken.
|
71854 | const OpDesc b_desc = b_op->get_desc(); | |
337 | ||||
338 |
3/4✓ Branch 1 taken 71854 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 35001 times.
✓ Branch 4 taken 36853 times.
|
2/2✓ Decision 'true' taken 35001 times.
✓ Decision 'false' taken 36853 times.
|
71854 | if (!b_desc.is_oneway()) { |
339 | // if A = B.dagger(), AB = I | |||
340 | // This method cannot detect matches between rotation gates. | |||
341 | // Rotation gates are covered by the rotation gate combiner, everything | |||
342 | // else in this. | |||
343 |
4/6✓ Branch 3 taken 35001 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 35001 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 30 times.
✓ Branch 11 taken 34971 times.
|
2/2✓ Decision 'true' taken 30 times.
✓ Decision 'false' taken 34971 times.
|
35001 | if (*b_op->dagger() == *op) { |
344 |
1/2✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
|
30 | bin.push_back(vert); | |
345 |
1/2✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
|
30 | bin.push_back(b); | |
346 |
1/2✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
|
30 | VertexVec last_verts = circ.get_predecessors(vert); | |
347 |
2/2✓ Branch 4 taken 30 times.
✓ Branch 5 taken 30 times.
|
2/2✓ Decision 'true' taken 30 times.
✓ Decision 'false' taken 30 times.
|
60 | for (const Vertex &l : last_verts) { |
348 |
2/4✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 30 times.
✗ Branch 6 not taken.
|
30 | new_affected_verts.insert({im.at(l), l}); | |
349 | } | |||
350 |
1/2✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
|
30 | VertexList to_detach{vert, b}; | |
351 | // detached from circuit but not removed from graph | |||
352 |
1/2✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
|
30 | circ.remove_vertices( | |
353 | to_detach, Circuit::GraphRewiring::Yes, | |||
354 | Circuit::VertexDeletion::No); | |||
355 | 30 | return true; | ||
356 |
2/4✓ Branch 3 taken 34971 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 34971 times.
✗ Branch 6 not taken.
|
2/2✓ Decision 'true' taken 34971 times.
✓ Decision 'false' taken 30 times.
|
35001 | } else if (desc.is_rotation()) { |
357 | // combine two rotation gates together, then if the combined | |||
358 | // operation is the identity up to phase, remove from circuit | |||
359 |
4/6✓ Branch 1 taken 34971 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 34971 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 135 times.
✓ Branch 7 taken 34836 times.
|
2/2✓ Decision 'true' taken 135 times.
✓ Decision 'false' taken 34836 times.
|
34971 | if (b_desc.type() == desc.type()) { |
360 |
2/4✓ Branch 2 taken 135 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 135 times.
✗ Branch 7 not taken.
|
135 | Expr expr1 = op->get_params()[0]; | |
361 |
2/4✓ Branch 2 taken 135 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 135 times.
✗ Branch 7 not taken.
|
135 | Expr expr2 = b_op->get_params()[0]; | |
362 |
1/2✓ Branch 1 taken 135 times.
✗ Branch 2 not taken.
|
135 | VertexVec last_verts = circ.get_predecessors(vert); | |
363 |
2/2✓ Branch 4 taken 135 times.
✓ Branch 5 taken 135 times.
|
2/2✓ Decision 'true' taken 135 times.
✓ Decision 'false' taken 135 times.
|
270 | for (const Vertex &l : last_verts) { |
364 |
2/4✓ Branch 1 taken 135 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 135 times.
✗ Branch 6 not taken.
|
135 | new_affected_verts.insert({im.at(l), l}); | |
365 | } | |||
366 |
1/2✓ Branch 1 taken 135 times.
✗ Branch 2 not taken.
|
135 | circ.remove_vertex( | |
367 | b, Circuit::GraphRewiring::Yes, Circuit::VertexDeletion::No); | |||
368 |
1/2✓ Branch 1 taken 135 times.
✗ Branch 2 not taken.
|
135 | bin.push_back(b); | |
369 |
2/4✓ Branch 1 taken 135 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 135 times.
✗ Branch 6 not taken.
|
405 | std::vector<Expr> params_new = {expr1 + expr2}; | |
370 |
2/4✓ Branch 2 taken 135 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 135 times.
✗ Branch 6 not taken.
|
135 | Op_ptr op_new = get_op_ptr(desc.type(), params_new, ins.size()); | |
371 |
1/2✓ Branch 2 taken 135 times.
✗ Branch 3 not taken.
|
135 | std::optional<double> a = op_new->is_identity(); | |
372 |
2/2✓ Branch 1 taken 42 times.
✓ Branch 2 taken 93 times.
|
2/2✓ Decision 'true' taken 42 times.
✓ Decision 'false' taken 93 times.
|
135 | if (a) { |
373 |
1/2✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
|
42 | bin.push_back(vert); | |
374 |
1/2✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
|
42 | circ.remove_vertex( | |
375 | vert, Circuit::GraphRewiring::Yes, Circuit::VertexDeletion::No); | |||
376 |
3/6✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 42 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 42 times.
✗ Branch 8 not taken.
|
42 | circ.add_phase(a.value()); | |
377 | } else { | |||
378 |
2/4✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 93 times.
✗ Branch 6 not taken.
|
93 | new_affected_verts.insert({im[vert], vert}); | |
379 |
1/2✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
|
93 | circ.dag[vert].op = op_new; | |
380 | } | |||
381 | 135 | return true; | ||
382 | 135 | } | ||
383 | } | |||
384 | } | |||
385 |
6/6✓ Branch 1 taken 71689 times.
✓ Branch 2 taken 165 times.
✓ Branch 4 taken 71689 times.
✓ Branch 5 taken 165 times.
✓ Branch 7 taken 71689 times.
✓ Branch 8 taken 165 times.
|
72184 | } | |
386 | 71689 | return false; | ||
387 | 147290 | } | ||
388 | ||||
389 | 37101 | static bool redundancy_removal(Circuit &circ) { | ||
390 | 37101 | bool success = false; | ||
391 | 37101 | bool found_redundancy = true; | ||
392 |
1/2✓ Branch 1 taken 37101 times.
✗ Branch 2 not taken.
|
37101 | IndexMap im = circ.index_map(); | |
393 | 37101 | std::set<IVertex> old_affected_verts; | ||
394 |
7/8✓ Branch 1 taken 37101 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 146354 times.
✓ Branch 5 taken 37101 times.
✓ Branch 7 taken 146354 times.
✓ Branch 8 taken 37101 times.
✓ Branch 10 taken 37101 times.
✓ Branch 11 taken 37101 times.
|
220556 | BGL_FORALL_VERTICES(v, circ.dag, DAG) { | |
395 |
2/4✓ Branch 1 taken 146354 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 146354 times.
✗ Branch 6 not taken.
|
146354 | old_affected_verts.insert({im.at(v), v}); | |
396 | } | |||
397 | 37101 | VertexList bin; | ||
398 |
2/2✓ Branch 0 taken 37941 times.
✓ Branch 1 taken 37101 times.
|
2/2✓ Decision 'true' taken 37941 times.
✓ Decision 'false' taken 37101 times.
|
75042 | while (found_redundancy) { |
399 | 37941 | std::set<IVertex> new_affected_verts; | ||
400 |
2/2✓ Branch 5 taken 147290 times.
✓ Branch 6 taken 37941 times.
|
2/2✓ Decision 'true' taken 147290 times.
✓ Decision 'false' taken 37941 times.
|
185231 | for (const IVertex &p : old_affected_verts) { |
401 |
1/2✓ Branch 1 taken 147290 times.
✗ Branch 2 not taken.
|
147290 | remove_redundancy(circ, p.second, bin, new_affected_verts, im); | |
402 | } | |||
403 | 37941 | found_redundancy = new_affected_verts.size() != 0; | ||
404 | 37941 | success |= found_redundancy; | ||
405 |
1/2✓ Branch 1 taken 37941 times.
✗ Branch 2 not taken.
|
37941 | old_affected_verts = new_affected_verts; | |
406 | 37941 | } | ||
407 |
1/2✓ Branch 1 taken 37101 times.
✗ Branch 2 not taken.
|
37101 | circ.remove_vertices( | |
408 | bin, Circuit::GraphRewiring::No, Circuit::VertexDeletion::Yes); | |||
409 | 37101 | return success; | ||
410 | 37101 | } | ||
411 | ||||
412 | } // namespace Transforms | |||
413 | ||||
414 | } // namespace tket | |||
415 |