tket
Loading...
Searching...
No Matches
RedundancyRemoval.cpp
Go to the documentation of this file.
1// Copyright Quantinuum
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 <optional>
16
20
21namespace tket::Transforms {
22
23// A helper struct for holding which vertices have been detached (a.k.a bin)
24// the predecessors of those vertices
25struct VertexDetachmentInfo {
26 VertexVec detachedVertices;
27 VertexVec detachedVertexPredecessors;
28};
29
30static void detach_vertex(
31 Circuit &circuit, const Vertex &vertex,
32 VertexDetachmentInfo &detachmentInfo) {
33 detachmentInfo.detachedVertices.emplace_back(vertex);
34 auto &vertexPredecessors = detachmentInfo.detachedVertexPredecessors;
35 const auto &newVertexPredecessors = circuit.get_predecessors(vertex);
36 vertexPredecessors.insert(
37 vertexPredecessors.cend(), newVertexPredecessors.cbegin(),
38 newVertexPredecessors.cend());
39 circuit.remove_vertex(
41}
42
43static void detach_vertex_and_successor(
44 Circuit &circuit, const Vertex &vertex, const Vertex &successor,
45 VertexDetachmentInfo &detachmentInfo) {
46 detachmentInfo.detachedVertices.emplace_back(vertex);
47 detachmentInfo.detachedVertices.emplace_back(successor);
48 auto &vertexPredecessors = detachmentInfo.detachedVertexPredecessors;
49 const auto &newVertexPredecessors = circuit.get_predecessors(vertex);
50 vertexPredecessors.insert(
51 vertexPredecessors.cend(), newVertexPredecessors.cbegin(),
52 newVertexPredecessors.cend());
53 circuit.remove_vertices(
56}
57
58static bool try_detach_identity(
59 Circuit &circuit, const Vertex &vertex,
60 VertexDetachmentInfo &detachmentInfo) {
61 auto vertex_operator = circuit.get_Op_ptr_from_Vertex(vertex);
62 if (auto phase = vertex_operator->is_identity()) {
63 circuit.add_phase(phase.value());
64 detach_vertex(circuit, vertex, detachmentInfo);
65 return true;
66 }
67 return false;
68}
69
70static bool vertex_is_a_measurement(
71 const Circuit &circuit, Vertex const &vertex) {
72 return circuit.get_OpType_from_Vertex(vertex) == OpType::Measure;
73}
74
75static bool
76vertex_is_succeeded_only_by_z_basis_measurements_with_which_it_commutes(
77 const Circuit &circuit, const Vertex &vertex) {
78 // If vertex has classical out edges, no need to continue
79 if (circuit.n_out_edges_of_type(vertex, EdgeType::Classical) != 0)
80 return false;
81 auto successors = circuit.get_successors(vertex);
82 std::vector<port_t> ports(successors.size());
83 std::iota(ports.begin(), ports.end(), 0);
84
85 return std::all_of(ports.cbegin(), ports.cend(), [&](port_t port) {
86 return vertex_is_a_measurement(circuit, successors[port]) &&
87 circuit.commutes_with_basis(vertex, Pauli::Z, port);
88 });
89}
90
91static bool try_detach_zbasis_commuting_vertex(
92 Circuit &circuit, const Vertex &vertex,
93 VertexDetachmentInfo &detachmentInfo) {
94 if (vertex_is_succeeded_only_by_z_basis_measurements_with_which_it_commutes(
95 circuit, vertex)) {
96 detach_vertex(circuit, vertex, detachmentInfo);
97 return true;
98 }
99 return false;
100}
101
102static bool port_ordering_is_compatible(
103 Circuit &circuit, const Vertex &vertex, const Vertex &successor) {
104 const Op_ptr vertex_op = circuit.get_Op_ptr_from_Vertex(vertex);
105 // Vertex port must be symmetrically equivalent to successor port for any edge
106 // between them Examples:
107 // CX[0,1]==CX[0,1] passes test because ports line up
108 // CX[0,1]==CX[1,0] fails test because ports don't line up and CX is not
109 // invariant with respect to exchange of qubits CZ[0,1]==CZ[1,0] passes test
110 // because CZ is invariant with respect to exchange of qubits
111 for (const Edge &in : circuit.get_in_edges(successor)) {
112 auto source_port = circuit.get_source_port(in);
113 auto target_port = circuit.get_target_port(in);
114 if (not vertex_op->has_symmetry(source_port, target_port)) {
115 return false;
116 }
117 }
118 return true;
119}
120
121static bool preliminary_vertex_successor_checks_pass(
122 Circuit &circuit, const Vertex &vertex) {
123 // check that the classical edges match up correctly
124 if (circuit.n_in_edges_of_type(vertex, EdgeType::Boolean) != 0) return false;
125
126 // check that both the vertex and its successor have each other and only each
127 // other
128 auto successors = circuit.get_successors(vertex);
129 if (successors.size() != 1) return false;
130 auto successor = successors[0];
131 if (circuit.get_predecessors(successor).size() != 1) return false;
132
133 // check that successor has adjoint
134 if (circuit.get_Op_ptr_from_Vertex(successor)->get_desc().is_oneway()) {
135 return false;
136 }
137
138 return port_ordering_is_compatible(circuit, vertex, successor);
139}
140
141static bool try_detach_both_because_successor_is_adjoint(
142 Circuit &circuit, Vertex const &vertex, Vertex const &successor,
143 VertexDetachmentInfo &detachmentInfo) {
144 const Op_ptr successor_op = circuit.get_Op_ptr_from_Vertex(successor);
145 const Op_ptr vertex_op = circuit.get_Op_ptr_from_Vertex(vertex);
146 if (*vertex_op == *successor_op->dagger()) {
147 detach_vertex_and_successor(circuit, vertex, successor, detachmentInfo);
148 return true;
149 }
150 return false;
151}
152
153static bool try_join_rotations_and_detach_successor(
154 Circuit &circuit, Vertex const &vertex, Vertex const &successor,
155 VertexDetachmentInfo &detachmentInfo) {
156 const Op_ptr successor_op = circuit.get_Op_ptr_from_Vertex(successor);
157 const OpDesc successor_op_descriptor = successor_op->get_desc();
158 const Op_ptr vertex_op = circuit.get_Op_ptr_from_Vertex(vertex);
159 const OpDesc vertex_op_descriptor = vertex_op->get_desc();
160
161 // check vertex and successor are same rotation type
162 if (not(vertex_op_descriptor.is_rotation() &&
163 vertex_op_descriptor.type() == successor_op_descriptor.type())) {
164 return false;
165 }
166
167 // replace vertex with combined rotation
168 auto expr1 = vertex_op->get_params()[0];
169 auto expr2 = successor_op->get_params()[0];
170 Op_ptr op_new = get_op_ptr(
171 vertex_op_descriptor.type(), {expr1 + expr2},
172 circuit.get_in_edges(successor).size());
173 circuit.dag[vertex].op = op_new;
174
175 // detach successor only (this adds vertex to list of vertices to check again,
176 // so will be removed later if is identity)
177 detach_vertex(circuit, successor, detachmentInfo);
178 return true;
179}
180
181static bool try_detach_single_vertex(
182 Circuit &circuit, const Vertex &vertex,
183 VertexDetachmentInfo &detachmentInfo) {
184 if (try_detach_identity(circuit, vertex, detachmentInfo)) {
185 return true;
186 }
187 if (try_detach_zbasis_commuting_vertex(circuit, vertex, detachmentInfo)) {
188 return true;
189 }
190 return false;
191}
192
193static bool try_detach_vertex_and_successor(
194 Circuit &circuit, const Vertex &vertex,
195 VertexDetachmentInfo &detachmentInfo) {
196 if (not preliminary_vertex_successor_checks_pass(circuit, vertex))
197 return false;
198 auto successor = circuit.get_successors(vertex)[0];
199 if (try_detach_both_because_successor_is_adjoint(
200 circuit, vertex, successor, detachmentInfo)) {
201 return true;
202 }
203 if (try_join_rotations_and_detach_successor(
204 circuit, vertex, successor, detachmentInfo)) {
205 return true;
206 }
207 return false;
208}
209
210static bool is_apriori_not_detachable(
211 const Circuit &circuit, Vertex const &vertex) {
212 const OpDesc op_descriptor =
213 circuit.get_Op_ptr_from_Vertex(vertex)->get_desc();
214
215 if (!op_descriptor.is_gate()) return true;
216 if (op_descriptor.type() == OpType::Phase) return false;
217 if (circuit.n_out_edges(vertex) == 0 || circuit.n_in_edges(vertex) == 0) {
218 return true; // vertex is boundary or already detached
219 }
220 return false;
221}
222
223static bool try_detach_vertex(
224 Circuit &circuit, const Vertex &vertex,
225 VertexDetachmentInfo &detachmentInfo) {
226 if (is_apriori_not_detachable(circuit, vertex)) {
227 return false;
228 }
229 if (try_detach_single_vertex(circuit, vertex, detachmentInfo)) {
230 return true;
231 }
232 if (try_detach_vertex_and_successor(circuit, vertex, detachmentInfo)) {
233 return true;
234 }
235 return false;
236}
237
238static VertexDetachmentInfo detach_vertices_if_redundant(
239 Circuit &circuit, const VertexVec &vertices) {
240 VertexDetachmentInfo detachmentInfo;
241 for (const auto &vertex : vertices) {
242 try_detach_vertex(circuit, vertex, detachmentInfo);
243 }
244 return detachmentInfo;
245}
246
247static VertexSet detach_any_redundant_vertices(Circuit &circuit) {
248 VertexSet bin;
249 VertexVec verticesToCheckForRemoval = circuit.vertices_in_order();
250 while (!verticesToCheckForRemoval.empty()) {
251 auto detachmentInfo =
252 detach_vertices_if_redundant(circuit, verticesToCheckForRemoval);
253 bin.insert(
254 detachmentInfo.detachedVertices.cbegin(),
255 detachmentInfo.detachedVertices.cend());
256 std::swap(
257 verticesToCheckForRemoval, detachmentInfo.detachedVertexPredecessors);
258 }
259 return bin;
260}
261
262// this method annihilates all primitives next to each other (accounting for
263// previous annihilation)
264// also removes redundant non-classically controlled Z basis gates before a z
265// basis measurement so that eg. -H-X-X-H- always annihilates to -----
267 VertexSet bin = detach_any_redundant_vertices(circuit);
268 circuit.remove_vertices(
270 return !bin.empty();
271}
272
274
275} // namespace tket::Transforms
A circuit.
Definition Circuit.hpp:212
void remove_vertices(const VertexSet &surplus, GraphRewiring graph_rewiring, VertexDeletion vertex_deletion)
A transformation of a circuit that preserves its semantics.
Definition Transform.hpp:27
Transform remove_redundancies()
bool redundancy_removal(Circuit &circuit)
detail::vertex< T > vertex
Definition Utils.hpp:34
boost::graph_traits< DAG >::vertex_descriptor Vertex
Definition DAGDefs.hpp:70
@ Measure
Measure a qubit, producing a classical output.
std::list< Vertex > VertexList
Definition DAGDefs.hpp:74
Op_ptr get_op_ptr(OpType chosen_type, const Expr &param, unsigned n_qubits)
Get an operation with a given type, single parameter and qubit count.
std::unordered_set< Vertex > VertexSet
Definition DAGDefs.hpp:72
@ Classical
A wire carrying classical information, corresponding to some allocated Bit.
@ Boolean
A wire carrying a bit of classical information from a classical output port of one op to a classical ...
boost::graph_traits< DAG >::edge_descriptor Edge
Definition DAGDefs.hpp:88
std::shared_ptr< const Op > Op_ptr
Definition OpPtr.hpp:24
unsigned port_t
A specific entry or exit port of a vertex.
Definition Op.hpp:48
std::vector< Vertex > VertexVec
Definition DAGDefs.hpp:73