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 | //#define DEBUG | |||
16 | #include <algorithm> | |||
17 | #include <numeric> | |||
18 | #include <optional> | |||
19 | #include <queue> | |||
20 | ||||
21 | #include "Architecture/Architecture.hpp" | |||
22 | #include "Graphs/Utils.hpp" | |||
23 | #include "Placement.hpp" | |||
24 | ||||
25 | namespace tket { | |||
26 | ||||
27 | 61 | std::set<Qubit> interacting_qbs(const Circuit& circ) { | ||
28 | 61 | std::set<Qubit> qbs; | ||
29 |
3/4✓ Branch 1 taken 61 times.
✗ Branch 2 not taken.
✓ Branch 8 taken 1323 times.
✓ Branch 9 taken 61 times.
|
0/1? Decision couldn't be analyzed.
|
1384 | for (const Qubit& qb : circ.all_qubits()) { |
30 |
2/4✓ Branch 1 taken 1323 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1323 times.
✗ Branch 5 not taken.
|
1323 | Edge e = circ.get_nth_out_edge(circ.get_in(qb), 0); | |
31 |
2/4✓ Branch 1 taken 1323 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1323 times.
✗ Branch 5 not taken.
|
1323 | Vertex terminal = circ.target(circ.skip_irrelevant_edges(e)); | |
32 |
3/4✓ Branch 1 taken 1323 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1316 times.
✓ Branch 4 taken 7 times.
|
2/2✓ Decision 'true' taken 1316 times.
✓ Decision 'false' taken 7 times.
|
1323 | if (!circ.detect_final_Op(terminal)) { |
33 |
1/2✓ Branch 1 taken 1316 times.
✗ Branch 2 not taken.
|
1316 | qbs.insert(qb); | |
34 | } | |||
35 | 61 | } | ||
36 | ||||
37 | 61 | return qbs; | ||
38 | } | |||
39 | ||||
40 | 61 | PlacementFrontier::PlacementFrontier(const Circuit& _circ) : circ(_circ) { | ||
41 | 61 | VertexVec input_slice; | ||
42 |
1/2✓ Branch 1 taken 61 times.
✗ Branch 2 not taken.
|
61 | quantum_in_edges = std::make_shared<unit_frontier_t>(); | |
43 |
1/2✓ Branch 1 taken 61 times.
✗ Branch 2 not taken.
|
61 | boolean_in_edges = std::make_shared<b_frontier_t>(); | |
44 | ||||
45 |
3/4✓ Branch 1 taken 61 times.
✗ Branch 2 not taken.
✓ Branch 7 taken 1323 times.
✓ Branch 8 taken 61 times.
|
0/1? Decision couldn't be analyzed.
|
1384 | for (const Qubit& qb : circ.all_qubits()) { |
46 |
1/2✓ Branch 1 taken 1323 times.
✗ Branch 2 not taken.
|
1323 | Vertex input = circ.get_in(qb); | |
47 |
1/2✓ Branch 1 taken 1323 times.
✗ Branch 2 not taken.
|
1323 | input_slice.push_back(input); | |
48 |
1/2✓ Branch 1 taken 1323 times.
✗ Branch 2 not taken.
|
1323 | Edge candidate = circ.get_nth_out_edge(input, 0); | |
49 |
2/4✓ Branch 2 taken 1323 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1323 times.
✗ Branch 7 not taken.
|
1323 | quantum_in_edges->insert({qb, circ.skip_irrelevant_edges(candidate)}); | |
50 | 61 | } | ||
51 |
3/4✓ Branch 1 taken 61 times.
✗ Branch 2 not taken.
✓ Branch 8 taken 9 times.
✓ Branch 9 taken 61 times.
|
0/1? Decision couldn't be analyzed.
|
70 | for (const Bit& bit : circ.all_bits()) { |
52 |
1/2✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
|
9 | Vertex input = circ.get_in(bit); | |
53 |
1/2✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
|
9 | EdgeVec candidates = circ.get_nth_b_out_bundle(input, 0); | |
54 |
2/4✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 9 times.
✗ Branch 6 not taken.
|
9 | boolean_in_edges->insert({bit, candidates}); | |
55 | 70 | } | ||
56 | ||||
57 |
1/2✓ Branch 3 taken 61 times.
✗ Branch 4 not taken.
|
122 | CutFrontier next_cut = circ.next_cut(quantum_in_edges, boolean_in_edges); | |
58 | 61 | slice = next_cut.slice; | ||
59 | 61 | quantum_out_edges = next_cut.u_frontier; | ||
60 | 61 | } | ||
61 | ||||
62 | 253 | void PlacementFrontier::next_slicefrontier() { | ||
63 |
1/2✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
|
253 | quantum_in_edges = std::make_shared<unit_frontier_t>(); | |
64 |
1/2✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
|
253 | boolean_in_edges = std::make_shared<b_frontier_t>(); | |
65 |
5/8✓ Branch 5 taken 8611 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 8611 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 8864 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 8611 times.
✓ Branch 14 taken 253 times.
|
0/1? Decision couldn't be analyzed.
|
8864 | for (const std::pair<UnitID, Edge>& pair : quantum_out_edges->get<TagKey>()) { |
66 |
1/2✓ Branch 1 taken 8611 times.
✗ Branch 2 not taken.
|
8611 | Edge new_e = circ.skip_irrelevant_edges(pair.second); | |
67 |
1/2✓ Branch 3 taken 8611 times.
✗ Branch 4 not taken.
|
8611 | quantum_in_edges->insert({pair.first, new_e}); | |
68 |
1/2✓ Branch 1 taken 8611 times.
✗ Branch 2 not taken.
|
8611 | Vertex targ = circ.target(new_e); | |
69 | EdgeVec targ_classical_ins = | |||
70 |
1/2✓ Branch 1 taken 8611 times.
✗ Branch 2 not taken.
|
8611 | circ.get_in_edges_of_type(targ, EdgeType::Boolean); | |
71 |
2/4✓ Branch 2 taken 8611 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 8611 times.
✗ Branch 6 not taken.
|
17222 | boolean_in_edges->insert( | |
72 |
3/6✓ Branch 1 taken 8611 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 8611 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 8611 times.
✗ Branch 9 not taken.
|
17222 | {Bit("frontier_bit", pair.first.index()), targ_classical_ins}); | |
73 | 8611 | } | ||
74 | ||||
75 |
1/2✓ Branch 3 taken 253 times.
✗ Branch 4 not taken.
|
506 | CutFrontier next_cut = circ.next_cut(quantum_in_edges, boolean_in_edges); | |
76 | 253 | slice = next_cut.slice; | ||
77 | 253 | quantum_out_edges = next_cut.u_frontier; | ||
78 | 253 | } | ||
79 | ||||
80 | 52 | QubitGraph monomorph_interaction_graph( | ||
81 | const Circuit& circ, const unsigned max_edges, unsigned depth_limit) { | |||
82 |
1/2✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
|
52 | std::set<Qubit> qubits_considered = interacting_qbs(circ); | |
83 | ||||
84 |
2/4✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 52 times.
✗ Branch 5 not taken.
|
52 | QubitGraph q_graph(circ.all_qubits()); | |
85 | ||||
86 |
1/2✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
|
52 | PlacementFrontier current_sf(circ); | |
87 | 52 | unsigned count_edges = 0; | ||
88 | 259 | for (unsigned slice = 0; | ||
89 |
2/2✓ Branch 0 taken 224 times.
✓ Branch 1 taken 12 times.
|
236 | slice < depth_limit && count_edges < max_edges && | |
90 |
7/8✓ Branch 0 taken 236 times.
✓ Branch 1 taken 23 times.
✓ Branch 4 taken 207 times.
✓ Branch 5 taken 17 times.
✓ Branch 7 taken 207 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 207 times.
✓ Branch 10 taken 52 times.
|
495 | !current_sf.slice->empty() && qubits_considered.size() > 1; | |
91 | slice++) { | |||
92 |
2/2✓ Branch 6 taken 249 times.
✓ Branch 7 taken 207 times.
|
2/2✓ Decision 'true' taken 249 times.
✓ Decision 'false' taken 207 times.
|
456 | for (const Vertex& vert : *current_sf.slice) { |
93 |
1/2✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
|
249 | EdgeVec q_out_edges = circ.get_out_edges_of_type(vert, EdgeType::Quantum); | |
94 |
1/2✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
|
249 | Qubit qb1; | |
95 |
1/2✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
|
249 | Qubit qb2; | |
96 | 249 | for (const std::pair<UnitID, Edge>& pair : | ||
97 |
5/8✓ Branch 5 taken 5606 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 5606 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 5855 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 5606 times.
✓ Branch 14 taken 249 times.
|
6104 | current_sf.quantum_out_edges->get<TagKey>()) { | |
98 |
3/4✓ Branch 2 taken 5606 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 249 times.
✓ Branch 5 taken 5357 times.
|
2/2✓ Decision 'true' taken 249 times.
✓ Decision 'false' taken 5357 times.
|
5606 | if (pair.second == q_out_edges[0]) |
99 |
1/2✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
|
249 | qb1 = Qubit(pair.first); | |
100 |
3/4✓ Branch 2 taken 5357 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 249 times.
✓ Branch 5 taken 5108 times.
|
2/2✓ Decision 'true' taken 249 times.
✓ Decision 'false' taken 5108 times.
|
5357 | else if (pair.second == q_out_edges[1]) |
101 |
1/2✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
|
249 | qb2 = Qubit(pair.first); | |
102 | } | |||
103 |
8/10✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 215 times.
✓ Branch 4 taken 34 times.
✓ Branch 6 taken 215 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 207 times.
✓ Branch 9 taken 8 times.
✓ Branch 10 taken 207 times.
✓ Branch 11 taken 42 times.
|
2/2✓ Decision 'true' taken 207 times.
✓ Decision 'false' taken 42 times.
|
249 | if (!q_graph.edge_exists(qb1, qb2) && !q_graph.edge_exists(qb2, qb1)) { |
104 |
1/2✓ Branch 1 taken 207 times.
✗ Branch 2 not taken.
|
207 | q_graph.add_connection(qb1, qb2, slice + 1); | |
105 | 207 | count_edges++; | ||
106 | } | |||
107 | 249 | } | ||
108 | ||||
109 |
1/2✓ Branch 1 taken 207 times.
✗ Branch 2 not taken.
|
207 | current_sf.next_slicefrontier(); | |
110 | } | |||
111 |
1/2✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
|
52 | q_graph.remove_stray_nodes(); | |
112 | 104 | return q_graph; | ||
113 | 52 | } | ||
114 | ||||
115 | 9 | QubitGraph generate_interaction_graph( | ||
116 | const Circuit& circ, unsigned depth_limit) { | |||
117 |
1/2✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
|
9 | std::set<Qubit> qubits_considered = interacting_qbs(circ); | |
118 |
2/4✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
|
9 | QubitGraph q_graph(circ.all_qubits()); | |
119 |
1/2✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
|
9 | PlacementFrontier current_sf(circ); | |
120 | ||||
121 |
7/8✓ Branch 0 taken 52 times.
✓ Branch 1 taken 3 times.
✓ Branch 4 taken 46 times.
✓ Branch 5 taken 6 times.
✓ Branch 6 taken 46 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 46 times.
✓ Branch 9 taken 9 times.
|
0/1? Decision couldn't be analyzed.
|
101 | for (unsigned slice = 0; slice < depth_limit && !current_sf.slice->empty() && |
122 | 46 | qubits_considered.size() > 1; | ||
123 | slice++) { | |||
124 |
2/2✓ Branch 6 taken 62 times.
✓ Branch 7 taken 46 times.
|
2/2✓ Decision 'true' taken 62 times.
✓ Decision 'false' taken 46 times.
|
108 | for (const Vertex& vert : *current_sf.slice) { |
125 |
1/2✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
|
62 | EdgeVec q_out_edges = circ.get_out_edges_of_type(vert, EdgeType::Quantum); | |
126 |
1/2✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
|
62 | Qubit qb1; | |
127 |
1/2✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
|
62 | Qubit qb2; | |
128 | 62 | for (const std::pair<UnitID, Edge>& pair : | ||
129 |
5/8✓ Branch 5 taken 6394 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 6394 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 6456 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 6394 times.
✓ Branch 14 taken 62 times.
|
6518 | current_sf.quantum_out_edges->get<TagKey>()) { | |
130 |
3/4✓ Branch 2 taken 6394 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 62 times.
✓ Branch 5 taken 6332 times.
|
2/2✓ Decision 'true' taken 62 times.
✓ Decision 'false' taken 6332 times.
|
6394 | if (pair.second == q_out_edges[0]) |
131 |
1/2✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
|
62 | qb1 = Qubit(pair.first); | |
132 |
3/4✓ Branch 2 taken 6332 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 62 times.
✓ Branch 5 taken 6270 times.
|
2/2✓ Decision 'true' taken 62 times.
✓ Decision 'false' taken 6270 times.
|
6332 | else if (pair.second == q_out_edges[1]) |
133 |
1/2✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
|
62 | qb2 = Qubit(pair.first); | |
134 | } | |||
135 | const bool qb1_considered = | |||
136 |
1/2✓ Branch 2 taken 62 times.
✗ Branch 3 not taken.
|
62 | (qubits_considered.find(qb1) != qubits_considered.end()); | |
137 | const bool qb2_considered = | |||
138 |
1/2✓ Branch 2 taken 62 times.
✗ Branch 3 not taken.
|
62 | (qubits_considered.find(qb2) != qubits_considered.end()); | |
139 |
8/10✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 62 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 35 times.
✓ Branch 6 taken 27 times.
✓ Branch 7 taken 13 times.
✓ Branch 8 taken 22 times.
✓ Branch 9 taken 40 times.
✓ Branch 10 taken 22 times.
|
2/2✓ Decision 'true' taken 40 times.
✓ Decision 'false' taken 22 times.
|
62 | if ((qb2 != qb1) && (qb1_considered || qb2_considered)) { |
140 |
2/2✓ Branch 0 taken 13 times.
✓ Branch 1 taken 27 times.
|
2/2✓ Decision 'true' taken 13 times.
✓ Decision 'false' taken 27 times.
|
40 | if (!qb1_considered) { |
141 |
1/2✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
|
13 | qubits_considered.erase(qb2); | |
142 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 24 times.
|
2/2✓ Decision 'true' taken 3 times.
✓ Decision 'false' taken 24 times.
|
27 | } else if (!qb2_considered) { |
143 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | qubits_considered.erase(qb1); | |
144 |
2/4✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 24 times.
✗ Branch 4 not taken.
|
1/2✓ Decision 'true' taken 24 times.
✗ Decision 'false' not taken.
|
24 | } else if (!q_graph.edge_exists(qb1, qb2)) { |
145 |
1/2✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
|
24 | const unsigned out1 = q_graph.get_degree(qb1); | |
146 |
1/2✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
|
24 | const unsigned out2 = q_graph.get_degree(qb2); | |
147 |
1/2✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
|
24 | q_graph.add_connection(qb1, qb2, slice + 1); | |
148 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 12 times.
|
2/2✓ Decision 'true' taken 12 times.
✓ Decision 'false' taken 12 times.
|
24 | if (out1 == 1) { |
149 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | qubits_considered.erase(qb1); | |
150 | } | |||
151 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 15 times.
|
2/2✓ Decision 'true' taken 9 times.
✓ Decision 'false' taken 15 times.
|
24 | if (out2 == 1) { |
152 |
1/2✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
|
9 | qubits_considered.erase(qb2); | |
153 | } | |||
154 | } else { | |||
155 | ✗ | qubits_considered.erase(qb1); | ||
156 | ✗ | qubits_considered.erase(qb2); | ||
157 | } | |||
158 | } | |||
159 | 62 | } | ||
160 |
1/2✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
|
46 | current_sf.next_slicefrontier(); | |
161 | } | |||
162 |
1/2✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
|
9 | q_graph.remove_stray_nodes(); | |
163 | ||||
164 | 18 | return q_graph; | ||
165 | 9 | } | ||
166 | ||||
167 | 9 | QubitLineList qubit_lines(const Circuit& circ) { | ||
168 |
1/2✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
|
9 | const QubitGraph q_graph = generate_interaction_graph(circ); | |
169 |
3/4✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 8 times.
|
2/2✓ Decision 'true' taken 1 times.
✓ Decision 'false' taken 8 times.
|
9 | if (q_graph.get_all_edges_vec().size() == 0) { |
170 | 1 | return {}; | ||
171 | } | |||
172 | 8 | std::set<Qubit> all_qb; | ||
173 |
3/4✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 8 taken 472 times.
✓ Branch 9 taken 8 times.
|
0/1? Decision couldn't be analyzed.
|
480 | for (const Qubit& qb : circ.all_qubits()) { |
174 |
1/2✓ Branch 1 taken 472 times.
✗ Branch 2 not taken.
|
472 | all_qb.insert(qb); | |
175 | 8 | } | ||
176 | 8 | QubitLineList found_lines; | ||
177 | 8 | unsigned found_line_size = 0; | ||
178 |
2/4✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
|
8 | QubitGraph::UndirectedConnGraph graph = q_graph.get_undirected_connectivity(); | |
179 | do { | |||
180 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | auto u_line = graphs::longest_simple_path(graph); | |
181 | 16 | QubitLine found; | ||
182 |
2/4✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 16 times.
✗ Branch 7 not taken.
|
16 | std::transform( | |
183 | u_line.begin(), u_line.end(), std::back_inserter(found), | |||
184 | 35 | [&graph](auto v) -> Qubit { return graph[v]; }); | ||
185 | 16 | found_line_size = found.size(); | ||
186 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
|
2/2✓ Decision 'true' taken 8 times.
✓ Decision 'false' taken 8 times.
|
16 | if (found_line_size > 1) { |
187 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | found_lines.push_back(found); | |
188 |
2/2✓ Branch 5 taken 27 times.
✓ Branch 6 taken 8 times.
|
2/2✓ Decision 'true' taken 27 times.
✓ Decision 'false' taken 8 times.
|
35 | for (const auto& vertex : u_line) { |
189 |
1/2✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
|
27 | boost::clear_vertex(vertex, graph); | |
190 | } | |||
191 |
2/2✓ Branch 6 taken 27 times.
✓ Branch 7 taken 8 times.
|
2/2✓ Decision 'true' taken 27 times.
✓ Decision 'false' taken 8 times.
|
35 | for (Qubit q : found) { |
192 |
1/2✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
|
27 | all_qb.erase(q); | |
193 | 27 | } | ||
194 | } | |||
195 |
2/2✓ Branch 2 taken 8 times.
✓ Branch 3 taken 8 times.
|
2/2✓ Decision 'true' taken 8 times.
✓ Decision 'false' taken 8 times.
|
16 | } while (found_line_size > 1); |
196 | ||||
197 |
3/4✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 8 taken 472 times.
✓ Branch 9 taken 8 times.
|
0/1? Decision couldn't be analyzed.
|
480 | for (const Qubit& qb : circ.all_qubits()) { |
198 |
7/12✓ Branch 2 taken 472 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 445 times.
✓ Branch 6 taken 27 times.
✓ Branch 10 taken 445 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 445 times.
✗ Branch 14 not taken.
✓ Branch 17 taken 445 times.
✓ Branch 18 taken 445 times.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
|
2/2✓ Decision 'true' taken 8 times.
✓ Decision 'false' taken 909 times.
|
917 | if (all_qb.find(qb) != all_qb.end()) found_lines.push_back({qb}); |
199 | 8 | } | ||
200 | 8 | return found_lines; | ||
201 | 9 | } | ||
202 | ||||
203 | // remove a given number of nodes from the architecture, return a set of usable | |||
204 | // nodes (the remainder) | |||
205 | 8 | node_set_t best_nodes(Architecture& arc, unsigned n_remove) { | ||
206 |
1/2✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | node_set_t all_nodes = arc.nodes(); | |
207 | 8 | node_set_t bad_nodes; | ||
208 | // if there are nodes 'removed' already count them as bad nodes | |||
209 |
2/2✓ Branch 6 taken 473 times.
✓ Branch 7 taken 8 times.
|
2/2✓ Decision 'true' taken 473 times.
✓ Decision 'false' taken 8 times.
|
481 | for (Node n : all_nodes) { |
210 |
2/4✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 473 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 473 times.
|
473 | if (arc.get_degree(n) == 0) { |
211 | ✗ | bad_nodes.insert(n); | ||
212 | ✗ | n_remove--; | ||
213 | } | |||
214 | 473 | } | ||
215 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | node_set_t removed_nodes = arc.remove_worst_nodes(n_remove); | |
216 |
1/2✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
|
8 | bad_nodes.insert(removed_nodes.begin(), removed_nodes.end()); | |
217 | 8 | node_set_t good_nodes; // keep track of nodes of architecture actually used | ||
218 |
2/4✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 9 taken 8 times.
✗ Branch 10 not taken.
|
8 | std::set_difference( | |
219 | all_nodes.begin(), all_nodes.end(), bad_nodes.begin(), bad_nodes.end(), | |||
220 | std::inserter(good_nodes, good_nodes.begin())); | |||
221 | 16 | return good_nodes; | ||
222 | 8 | } | ||
223 | ||||
224 | // map qubit lines to node lines, erasing qubit lines as it goes | |||
225 | 8 | qubit_mapping_t map_lines( | ||
226 | QubitLineList& qb_lines, const std::vector<node_vector_t>& node_lines) { | |||
227 | 8 | qubit_mapping_t outmap; | ||
228 | ||||
229 | // go through all node lines | |||
230 |
2/2✓ Branch 1 taken 8 times.
✓ Branch 2 taken 8 times.
|
2/2✓ Decision 'true' taken 8 times.
✓ Decision 'false' taken 8 times.
|
16 | for (unsigned i = 0; i < node_lines.size(); i++) { |
231 |
1/2✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | node_vector_t node_lst = node_lines[i]; | |
232 |
2/2✓ Branch 4 taken 27 times.
✓ Branch 5 taken 8 times.
|
2/2✓ Decision 'true' taken 27 times.
✓ Decision 'false' taken 8 times.
|
35 | for (const Node& node : node_lst) { |
233 |
1/2✓ Branch 5 taken 27 times.
✗ Branch 6 not taken.
|
27 | outmap.insert({*qb_lines[i].begin(), node}); | |
234 |
1/2✓ Branch 5 taken 27 times.
✗ Branch 6 not taken.
|
27 | qb_lines[i].erase(qb_lines[i].begin()); | |
235 | } | |||
236 | 8 | } | ||
237 | 8 | return outmap; | ||
238 | } | |||
239 | ||||
240 | // trivially place qubit lines on to available nodes, return map | |||
241 | 8 | qubit_mapping_t place_qubit_lines( | ||
242 | const QubitLineList& qb_lines, node_set_t available_nodes) { | |||
243 | 8 | qubit_mapping_t outmap; | ||
244 | ||||
245 | 8 | node_set_t::iterator node_it = available_nodes.begin(); | ||
246 |
2/2✓ Branch 5 taken 8 times.
✓ Branch 6 taken 8 times.
|
2/2✓ Decision 'true' taken 8 times.
✓ Decision 'false' taken 8 times.
|
16 | for (const QubitLine& line : qb_lines) { |
247 |
1/2✗ Branch 5 not taken.
✓ Branch 6 taken 8 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 8 times.
|
8 | for (const Qubit& qb : line) { |
248 |
0/2✗ Decision 'true' not taken.
✗ Decision 'false' not taken.
|
✗ | if (node_it == available_nodes.end()) { | |
249 | ✗ | throw ArchitectureInvalidity("Not enough nodes to place all qubits."); | ||
250 | } | |||
251 | ✗ | outmap.insert({qb, *node_it}); | ||
252 | ✗ | ++node_it; | ||
253 | } | |||
254 | } | |||
255 | 16 | return outmap; | ||
256 | } | |||
257 | ||||
258 | // Determine intial qubit placement by requesting lines of architecture to place | |||
259 | // lines of qubits on | |||
260 | 8 | qubit_mapping_t lines_on_arc( | ||
261 | Architecture arc, QubitLineList qb_lines, unsigned n_qubits) { | |||
262 | 8 | unsigned difference = arc.n_nodes() - n_qubits; | ||
263 | // sort from longest to shortest | |||
264 |
1/2✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
|
8 | std::sort(qb_lines.begin(), qb_lines.end(), [](QubitLine x, QubitLine y) { | |
265 | 2429 | return (x.size() > y.size()); | ||
266 | }); | |||
267 | ||||
268 | // get rid of one qubit lines | |||
269 |
5/6✓ Branch 1 taken 453 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 445 times.
✓ Branch 6 taken 8 times.
✓ Branch 7 taken 445 times.
✓ Branch 8 taken 8 times.
|
0/1? Decision couldn't be analyzed.
|
453 | while (!qb_lines.empty() && qb_lines.back().size() < 2) { |
270 | 445 | difference++; | ||
271 | 445 | qb_lines.pop_back(); | ||
272 | } | |||
273 | ||||
274 | // remove poorly connected nodes, up to the number not used by mapping | |||
275 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | node_set_t unused_nodes = best_nodes(arc, difference); | |
276 | ||||
277 | // find lengths required | |||
278 | 8 | std::vector<unsigned> lengths; | ||
279 |
2/2✓ Branch 4 taken 8 times.
✓ Branch 5 taken 8 times.
|
2/2✓ Decision 'true' taken 8 times.
✓ Decision 'false' taken 8 times.
|
16 | for (const QubitLine& line : qb_lines) { |
280 |
1/2✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | lengths.push_back(line.size()); | |
281 | } | |||
282 | ||||
283 | // attempt to find lines of required length on architecture | |||
284 |
2/4✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
|
8 | std::vector<node_vector_t> node_lines = arc.get_lines(lengths); | |
285 | ||||
286 | // map qubit lines to node lines to some extent | |||
287 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | qubit_mapping_t outmap = map_lines(qb_lines, node_lines); | |
288 |
2/2✓ Branch 3 taken 27 times.
✓ Branch 4 taken 8 times.
|
2/2✓ Decision 'true' taken 27 times.
✓ Decision 'false' taken 8 times.
|
35 | for (qubit_mapping_t::iterator it = outmap.begin(); it != outmap.end(); |
289 | 27 | ++it) { | ||
290 |
1/2✓ Branch 2 taken 27 times.
✗ Branch 3 not taken.
|
27 | unused_nodes.erase(it->second); | |
291 | } | |||
292 | // map remain remaining qubit lines trivially | |||
293 |
2/4✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
|
8 | qubit_mapping_t remainder_map = place_qubit_lines(qb_lines, unused_nodes); | |
294 |
1/2✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
|
8 | outmap.insert(remainder_map.begin(), remainder_map.end()); | |
295 | ||||
296 | 16 | return outmap; | ||
297 | 8 | } | ||
298 | ||||
299 | // Uses line placement method to produce a suitable qubit_mapping_t for initial | |||
300 | // placement Note that arc is passed by value, since this function modifies it. | |||
301 | ✗ | qubit_mapping_t line_placement(const Circuit& circ, Architecture arc) { | ||
302 | ✗ | QubitLineList qb_lines = qubit_lines(circ); | ||
303 |
0/2✗ Decision 'true' not taken.
✗ Decision 'false' not taken.
|
✗ | if (qb_lines.empty()) { | |
304 | ✗ | return qubit_mapping_t(); | ||
305 | } else { | |||
306 | ✗ | unsigned n_qb = circ.n_qubits(); | ||
307 | ✗ | return lines_on_arc(arc, qb_lines, n_qb); | ||
308 | } | |||
309 | } | |||
310 | ||||
311 | // calculate a cost value for map being considered | |||
312 | 1138 | double Monomorpher::map_cost(const qubit_bimap_t& n_map) { | ||
313 | 1138 | double cost = 0.0; | ||
314 | 1138 | const int approx_depth = circ.n_gates() / circ.n_qubits() + 1; | ||
315 | // constants for scaling single qubit error | |||
316 | 1138 | constexpr double c1 = 0.5; | ||
317 | 1138 | constexpr double d1 = 1 - 1 / c1; | ||
318 |
8/14✓ Branch 1 taken 1138 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1138 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4232 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 4232 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 4232 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 5370 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 4232 times.
✓ Branch 19 taken 1138 times.
|
0/1? Decision couldn't be analyzed.
|
5370 | for (auto [qb, node] : n_map) { |
319 | // add fidelities of edges from node, weighted by if edge is used by | |||
320 | // interaction graph | |||
321 |
1/2✓ Branch 1 taken 4232 times.
✗ Branch 2 not taken.
|
4232 | node_set_t neighs = arc.get_neighbour_nodes(node); | |
322 | 4232 | double edge_sum = 1.0; | ||
323 |
2/2✓ Branch 6 taken 13412 times.
✓ Branch 7 taken 4232 times.
|
2/2✓ Decision 'true' taken 13412 times.
✓ Decision 'false' taken 4232 times.
|
17644 | for (Node nei : neighs) { |
324 | 13412 | double fwd_edge_weighting = 1.0, bck_edge_weighting = 1.0; | ||
325 | // check if neighbour node is mapped | |||
326 |
1/2✓ Branch 1 taken 13412 times.
✗ Branch 2 not taken.
|
13412 | r_const_iterator_t nei_qb_it = n_map.right.find(nei); | |
327 |
4/6✓ Branch 1 taken 13412 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 13412 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6640 times.
✓ Branch 7 taken 6772 times.
|
2/2✓ Decision 'true' taken 6772 times.
✓ Decision 'false' taken 6640 times.
|
13412 | if (nei_qb_it == n_map.right.end()) continue; |
328 | // if ( nei_qb_it != n_map.end()){ | |||
329 |
2/4✓ Branch 1 taken 6772 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6772 times.
✗ Branch 5 not taken.
|
6772 | unsigned edge_val = q_graph.get_connection_weight(qb, nei_qb_it->second); | |
330 | // check if either directed interaction exists | |||
331 | 6188 | auto place_interactions_boost = [&](unsigned edge_v) { | ||
332 | 6188 | return config.depth_limit - edge_v + 1; | ||
333 | 6772 | }; | ||
334 | // if edge is used by interaction in mapping, weight edge higher | |||
335 |
2/2✓ Branch 0 taken 3094 times.
✓ Branch 1 taken 3678 times.
|
2/2✓ Decision 'true' taken 3094 times.
✓ Decision 'false' taken 3678 times.
|
6772 | if (edge_val) { |
336 | 3094 | fwd_edge_weighting += place_interactions_boost(edge_val); | ||
337 | } else { | |||
338 |
2/4✓ Branch 1 taken 3678 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3678 times.
✗ Branch 5 not taken.
|
3678 | edge_val = q_graph.get_connection_weight(nei_qb_it->second, qb); | |
339 |
2/2✓ Branch 0 taken 3094 times.
✓ Branch 1 taken 584 times.
|
2/2✓ Decision 'true' taken 3094 times.
✓ Decision 'false' taken 584 times.
|
3678 | if (edge_val) { |
340 | 3094 | bck_edge_weighting += place_interactions_boost(edge_val); | ||
341 | } | |||
342 | } | |||
343 | // } | |||
344 |
1/2✓ Branch 2 taken 6772 times.
✗ Branch 3 not taken.
|
6772 | gate_error_t fwd_error = characterisation.get_error({node, nei}); | |
345 |
1/2✓ Branch 2 taken 6772 times.
✗ Branch 3 not taken.
|
6772 | gate_error_t bck_error = characterisation.get_error({nei, node}); | |
346 | 6772 | edge_sum += fwd_edge_weighting * (1.0 - fwd_error); | ||
347 | 6772 | edge_sum += bck_edge_weighting * (1.0 - bck_error); | ||
348 |
2/2✓ Branch 1 taken 6772 times.
✓ Branch 2 taken 6640 times.
|
13412 | } | |
349 | ||||
350 | // bigger edge sum -> smaller cost | |||
351 | 4232 | cost += 1.0 / (edge_sum); | ||
352 | ||||
353 | // add error rate of node | |||
354 |
1/2✓ Branch 1 taken 4232 times.
✗ Branch 2 not taken.
|
4232 | gate_error_t single_error = characterisation.get_error(node); | |
355 | 4232 | cost += d1 + 1.0 / ((1.0 - single_error) + c1); | ||
356 |
1/2✓ Branch 1 taken 4232 times.
✗ Branch 2 not taken.
|
4232 | readout_error_t readout_error = characterisation.get_readout_error(node); | |
357 | 4232 | cost += (d1 + 1.0 / ((1.0 - readout_error) + c1)) / (approx_depth * 20); | ||
358 | // TODO add readout weighting to PlacementConfig? | |||
359 | 4232 | } | ||
360 | ||||
361 | 1138 | return cost; | ||
362 | } | |||
363 | ||||
364 | 18 | std::vector<MapCost> Monomorpher::place(unsigned max_return) { | ||
365 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 18 times.
|
18 | if (max_return < 1) |
366 |
0/1? Decision couldn't be analyzed.
|
✗ | throw PlacementError("Max return maps for place must be at least 1."); | |
367 |
1/2✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
|
18 | const unsigned interacting_nodes = q_graph.n_connected(); | |
368 |
5/8✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 17 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 18 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 19 times.
|
19 | if ((circ.n_gates() / circ.n_qubits()) >= config.arc_contraction_ratio && |
369 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
|
1 | circ.n_qubits() > 3) { | |
370 | ✗ | best_nodes(arc, arc.n_nodes() - interacting_nodes); | ||
371 | } | |||
372 | ||||
373 | std::vector<qubit_bimap_t> potential_maps = monomorphism_edge_break( | |||
374 |
2/2✓ Branch 1 taken 17 times.
✓ Branch 2 taken 1 times.
|
18 | arc, q_graph, config.monomorphism_max_matches, config.timeout); | |
375 | 17 | std::vector<MapCost> map_costs; | ||
376 |
2/2✓ Branch 1 taken 1138 times.
✓ Branch 2 taken 17 times.
|
2/2✓ Decision 'true' taken 1138 times.
✓ Decision 'false' taken 17 times.
|
1155 | for (unsigned i = 0; i < potential_maps.size(); i++) { |
377 | 1138 | qubit_bimap_t& chosen = potential_maps[i]; | ||
378 |
1/2✓ Branch 1 taken 1138 times.
✗ Branch 2 not taken.
|
1138 | double cost = map_cost(chosen); | |
379 | 1138 | qubit_mapping_t converted_map; | ||
380 |
7/12✓ Branch 1 taken 1138 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1138 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4232 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 4232 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 5370 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 4232 times.
✓ Branch 17 taken 1138 times.
|
0/1? Decision couldn't be analyzed.
|
5370 | for (auto [qb, node] : chosen.left) { |
381 |
1/2✓ Branch 1 taken 4232 times.
✗ Branch 2 not taken.
|
4232 | converted_map[qb] = node; | |
382 | 4232 | } | ||
383 |
2/4✓ Branch 1 taken 1138 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1138 times.
✗ Branch 5 not taken.
|
1138 | map_costs.push_back({converted_map, cost}); | |
384 | 1138 | } | ||
385 | ||||
386 | 17 | max_return = std::min(static_cast<unsigned>(map_costs.size()), max_return); | ||
387 |
1/2✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
|
34 | std::nth_element( | |
388 | 17 | map_costs.begin(), map_costs.begin() + max_return - 1, map_costs.end()); | ||
389 |
1/2✓ Branch 5 taken 17 times.
✗ Branch 6 not taken.
|
17 | const MapCost nth_map = *(map_costs.begin() + max_return - 1); | |
390 | 17 | for (std::vector<MapCost>::iterator it = map_costs.begin(); | ||
391 |
2/2✓ Branch 2 taken 1138 times.
✓ Branch 3 taken 17 times.
|
1155 | it != map_costs.end();) { | |
392 |
2/2✓ Branch 2 taken 527 times.
✓ Branch 3 taken 611 times.
|
2/2✓ Decision 'true' taken 527 times.
✓ Decision 'false' taken 611 times.
|
1138 | if (*it > nth_map) { |
393 |
1/2✓ Branch 2 taken 527 times.
✗ Branch 3 not taken.
|
527 | it = map_costs.erase(it); | |
394 | } else { | |||
395 | 611 | ++it; | ||
396 | } | |||
397 | } | |||
398 | ||||
399 |
3/4✓ Branch 1 taken 11 times.
✓ Branch 2 taken 6 times.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
|
0/1? Decision couldn't be analyzed.
|
17 | if (max_return < map_costs.size()) map_costs.resize(max_return); |
400 | 34 | return map_costs; | ||
401 | 17 | } | ||
402 | ||||
403 | } // namespace tket | |||
404 |