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 | #pragma once | |||
16 | ||||
17 | #include "ZX/ZXDiagramImpl.hpp" | |||
18 | ||||
19 | namespace tket { | |||
20 | ||||
21 | namespace zx { | |||
22 | ||||
23 | // Forward declare Rewrite, ZXDiagramPybind, Flow for friend access | |||
24 | class Rewrite; | |||
25 | class ZXDiagramPybind; | |||
26 | class Flow; | |||
27 | ||||
28 | class ZXDiagram { | |||
29 | private: | |||
30 | /** | |||
31 | * Underlying representation | |||
32 | */ | |||
33 | ||||
34 | // Underlying graph | |||
35 | std::unique_ptr<ZXGraph> graph; | |||
36 | ||||
37 | /** | |||
38 | * Boundary vertices in addressable order. | |||
39 | * This may include both Quantum and Classical boundaries. | |||
40 | * Each boundary vertex can be an Input, Output, or Open (for generic | |||
41 | * undirected boundary points). | |||
42 | */ | |||
43 | ZXVertVec boundary; | |||
44 | ||||
45 | // Global scalar for tracking during rewrites | |||
46 | Expr scalar; | |||
47 | ||||
48 | public: | |||
49 | /** | |||
50 | * Constructors & assignment operators for: | |||
51 | * - empty diagram, | |||
52 | * - empty diagram with specific input / output signatures | |||
53 | * - copy constructor | |||
54 | * - copy assignment | |||
55 | */ | |||
56 | ZXDiagram(); | |||
57 | ZXDiagram( | |||
58 | unsigned in, unsigned out, unsigned classical_in, unsigned classical_out); | |||
59 | ZXDiagram(const ZXDiagram& other); | |||
60 | ZXDiagram(ZXDiagram&& other); | |||
61 | ZXDiagram& operator=(const ZXDiagram& other); | |||
62 | ZXDiagram& operator=(ZXDiagram&& other); | |||
63 | ||||
64 | /** | |||
65 | * Getters & Setters | |||
66 | */ | |||
67 | // If `qtype` is given, then a copy of the boundary subset will be returned | |||
68 | ZXVertVec get_boundary( | |||
69 | std::optional<ZXType> type = std::nullopt, | |||
70 | std::optional<QuantumType> qtype = std::nullopt) const; | |||
71 | ||||
72 | // TODO discuss whether to expose these two | |||
73 | std::unique_ptr<ZXGraph>& get_graph(); | |||
74 | void add_boundary(ZXVert& vert); | |||
75 | ||||
76 | // Getting the global scalar and modifying by multiplication | |||
77 | const Expr& get_scalar() const; | |||
78 | void multiply_scalar(const Expr& sc); | |||
79 | ||||
80 | // Counting all vertices / wires in the diagram | |||
81 | unsigned n_vertices() const; | |||
82 | unsigned n_wires() const; | |||
83 | ||||
84 | // Count number of vertices with certain types & properties | |||
85 | unsigned count_vertices(ZXType type) const; | |||
86 | unsigned count_vertices(ZXType zxtype, QuantumType qtype) const; | |||
87 | unsigned count_wires(ZXWireType type) const; | |||
88 | ||||
89 | // Local properties on vertices and edges | |||
90 | unsigned degree(const ZXVert& v) const; | |||
91 | // Neighbours given in order of iteration through the underlying boost graph, | |||
92 | // which is deterministic from a given insertion order but need not be | |||
93 | // semantically relevant | |||
94 | ZXVertVec neighbours(const ZXVert& v) const; | |||
95 | // Incident wires given in order of iteration through the underlying boost | |||
96 | // graph, outedges (and self-loops) before inedges (ignoring self-loops) | |||
97 | WireVec adj_wires(const ZXVert& v) const; | |||
98 | // Wires given in the same order as `adj_wires(u)` | |||
99 | WireVec wires_between(const ZXVert& u, const ZXVert& v) const; | |||
100 | ||||
101 | /** | |||
102 | * Searches for an arbitrary wire between `va` and `vb`. | |||
103 | * If none exists, then `std::nullopt` is returned. | |||
104 | * `directed` controls the search for semantically undirected edges within | |||
105 | * the underlying directed graph structure and should be called with | |||
106 | * `UNDIRECTED` unless there is good reason to not. | |||
107 | */ | |||
108 | enum class WireSearchOption { UNDIRECTED, DIRECTED }; | |||
109 | std::optional<Wire> wire_between( | |||
110 | const ZXVert& va, const ZXVert& vb, | |||
111 | WireSearchOption directed = WireSearchOption::UNDIRECTED) const; | |||
112 | ||||
113 | Wire wire_at_port(const ZXVert& v, std::optional<unsigned> port) const; | |||
114 | ||||
115 | // Getting/setting vertex properties | |||
116 | ZXGen_ptr get_vertex_ZXGen_ptr(const ZXVert& v) const; | |||
117 | template < | |||
118 | typename T, typename = typename std::enable_if< | |||
119 | std::is_base_of<ZXGen, T>::value, bool>::type> | |||
120 | const T& get_vertex_ZXGen(const ZXVert& v) const; | |||
121 | std::string get_name(const ZXVert& v) const; | |||
122 | ZXType get_zxtype(const ZXVert& v) const; | |||
123 | std::optional<QuantumType> get_qtype(const ZXVert& v) const; | |||
124 | void set_vertex_ZXGen_ptr(const ZXVert& v, const ZXGen_ptr& op); | |||
125 | ||||
126 | // Getting/setting wire properies | |||
127 | WireProperties get_wire_info(const Wire& w) const; | |||
128 | QuantumType get_qtype(const Wire& w) const; | |||
129 | ZXWireType get_wire_type(const Wire& w) const; | |||
130 | ZXVert source(const Wire& w) const; | |||
131 | ZXVert target(const Wire& w) const; | |||
132 | std::optional<unsigned> source_port(const Wire& w) const; | |||
133 | std::optional<unsigned> target_port(const Wire& w) const; | |||
134 | // Returns the vertex on `w` at the other end from `u` | |||
135 | ZXVert other_end(const Wire& w, const ZXVert& u) const; | |||
136 | ZXVert vertex_at_end(const Wire& w, WireEnd we) const; | |||
137 | // Returns which end of `w` is connected to `u` | |||
138 | WireEnd end_of(const Wire& w, const ZXVert& u) const; | |||
139 | void set_wire_info(const Wire& w, const WireProperties& wp); | |||
140 | void set_wire_qtype(const Wire& w, QuantumType qtype); | |||
141 | void set_wire_type(const Wire& w, ZXWireType type); | |||
142 | ||||
143 | /** | |||
144 | * Shortcut utilities to detect whether `v` is a spider with a Pauli / | |||
145 | * proper Clifford phase | |||
146 | */ | |||
147 | bool is_pauli_spider(const ZXVert& v) const; | |||
148 | bool is_proper_clifford_spider(const ZXVert& v) const; | |||
149 | ||||
150 | /** | |||
151 | * Check for well-formedness of the internal graph. | |||
152 | * Checks: | |||
153 | * - Inputs/Outputs have degree 1 and all exist within the boundary. | |||
154 | * - Undirected vertices have no ports on incident edges. | |||
155 | * - Directed vertices have exactly one incident edge for each port. | |||
156 | * - All incident edges to vertex have a QuantumType compatible with that | |||
157 | * port. Throws an exception if one of the checks fail. Time complexity: O(V + | |||
158 | * E), for a graph with bounded degrees. | |||
159 | */ | |||
160 | void check_validity() const; | |||
161 | ||||
162 | /** | |||
163 | * Symbolic manipulation | |||
164 | */ | |||
165 | void symbol_substitution(const symbol_map_t& symbol_map); | |||
166 | void symbol_substitution( | |||
167 | const std::map<Sym, double, SymEngine::RCPBasicKeyLess>& symbol_map); | |||
168 | void symbol_substitution(const SymEngine::map_basic_basic& sub_map); | |||
169 | ||||
170 | // Set of all free symbols occurring in operation parameters | |||
171 | SymSet free_symbols() const; | |||
172 | ||||
173 | // Whether the diagram contains any symbolic parameters | |||
174 | bool is_symbolic() const; | |||
175 | ||||
176 | // Whether the diagram is graphlike (ZSpiders and H edges, Basics to | |||
177 | // boundaries) | |||
178 | bool is_graphlike() const; | |||
179 | ||||
180 | // Whether the diagram is MBQC (MBQC, Inputs, and Outputs, Basic to | |||
181 | // boundaries, H otherwise) | |||
182 | bool is_MBQC() const; | |||
183 | ||||
184 | /** | |||
185 | * Produces graphviz string, applying `highlights` to some vertices. | |||
186 | * Inputs: | |||
187 | * highlights: set of vertices to highlight (with a red star) in the | |||
188 | * visualisation. These should be valid vertices on `graph`. | |||
189 | **/ | |||
190 | std::string to_graphviz_str(const std::set<ZXVert>& highlights = {}) const; | |||
191 | ||||
192 | /** | |||
193 | * Diagram manipulation | |||
194 | */ | |||
195 | /** | |||
196 | * Adds a vertex with a given generator to the diagram. | |||
197 | * It is initially disconnected from any other vertex. | |||
198 | * Automatically adds boundary generators to the boundary. | |||
199 | */ | |||
200 | ZXVert add_vertex(ZXGen_ptr op); | |||
201 | ZXVert add_vertex(ZXType type, QuantumType qtype = QuantumType::Quantum); | |||
202 | ZXVert add_vertex( | |||
203 | ZXType type, const Expr& param, QuantumType qtype = QuantumType::Quantum); | |||
204 | ||||
205 | /** | |||
206 | * Adds a wire between `va` and `vb` by a `WireProperties` object. | |||
207 | * Uses `va` as the source and `vb` as the target in the underlying directed | |||
208 | * graph structure. | |||
209 | **/ | |||
210 | Wire add_wire(const ZXVert& va, const ZXVert& vb, const WireProperties& prop); | |||
211 | /** | |||
212 | * Adds a wire of specific types between `va` -> `vb`. | |||
213 | * `va_port`, `vb_port` are used by `DirectedZXGenerator` objects | |||
214 | * to specify which port on `va`, `vb` (respectively) the wire plugs into. | |||
215 | * | |||
216 | * See `ZXGenerator.hpp` --> `DirectedZXGenerator` for more | |||
217 | * information, including the definition of the port numbers. | |||
218 | * Returns: | |||
219 | * The successfully added `Wire`. | |||
220 | **/ | |||
221 | Wire add_wire( | |||
222 | const ZXVert& va, const ZXVert& vb, ZXWireType type = ZXWireType::Basic, | |||
223 | QuantumType qtype = QuantumType::Quantum, | |||
224 | std::optional<unsigned> va_port = std::nullopt, | |||
225 | std::optional<unsigned> vb_port = std::nullopt); | |||
226 | ||||
227 | /** | |||
228 | * Removes the vertex `v`, along with all wires connected to it. | |||
229 | * Boundary updated if vertex is in the `boundary`. | |||
230 | **/ | |||
231 | void remove_vertex(const ZXVert& v); | |||
232 | void remove_wire(const Wire& w); | |||
233 | ||||
234 | /** | |||
235 | * Removes exactly 1 wire that has property `prop` from vertex `va` -> `vb`. | |||
236 | * If there exist multiple edges matching the property, they are | |||
237 | * indistinguishable, so we just remove one arbitrarily. In this case, the | |||
238 | * only difference is the edge descriptor. If the user cares about | |||
239 | * preserving particular edge descriptors, remove_wire(const Wire&) should | |||
240 | * be used. | |||
241 | * Inputs: | |||
242 | * va, vb: (ordered) vertices to remove possible wire from | |||
243 | * prop: properties to look for in the wires between the two vertices | |||
244 | * directed: whether to try also with `vb` -> `va` (i.e. when we are | |||
245 | * trying to remove edges of unknown direction) | |||
246 | * Return value indicates whether an edge was removed. | |||
247 | **/ | |||
248 | bool remove_wire( | |||
249 | const ZXVert& va, const ZXVert& vb, const WireProperties& prop, | |||
250 | WireSearchOption directed = WireSearchOption::UNDIRECTED); | |||
251 | ||||
252 | /** | |||
253 | * Diagram conversion | |||
254 | */ | |||
255 | ||||
256 | /** | |||
257 | * Expands quantum vertices into pairs of classical vertices according to | |||
258 | * the "doubling" construction for CPM. | |||
259 | * New boundary vertices are ordered lexicographically by (b, c): | |||
260 | * - b boundary index in original diagram | |||
261 | * - c conjugate identifier | |||
262 | * + quantum boundaries are mapped to a pair with original and conjugated | |||
263 | * phases | |||
264 | * + unconjugated first | |||
265 | * + classical boundaries only have the unconjugated version | |||
266 | */ | |||
267 | ZXDiagram to_doubled_diagram() const; | |||
268 | ||||
269 | /** | |||
270 | * Expands classical boundary vertices into quantum boundaries via input | |||
271 | * extension with a ZSpider. | |||
272 | * Effectively prepends Z basis measurements and appends Z basis | |||
273 | * initialisations for each classical bit. | |||
274 | * This is used to map the entire semantics into a single CPTP-map, which | |||
275 | * can be represented by its Choi matrix. | |||
276 | */ | |||
277 | ZXDiagram to_quantum_embedding() const; | |||
278 | ||||
279 | /** | |||
280 | * Subdiagram | |||
281 | * | |||
282 | * Represents a closed region of the diagram by taking a cut through a set | |||
283 | * of edges. If the subdiagram were treated as a new ZXDiagram object: | |||
284 | * - The boundary order is as given by `boundary`. | |||
285 | * - All boundary vertices are treated as Open. | |||
286 | * - Boundary vertices inherit their QuantumType from the original Wire. | |||
287 | * - Boundary edges are all Basic (i.e. the Hadamard from Hadamard | |||
288 | * wires in the `boundary` list are treated as outside of the | |||
289 | * Subdiagram). | |||
290 | * Each wire in the boundary is tagged with the WireEnd facing the interior of | |||
291 | * the subdiagram. If a wire appears with both ends, they are treated as two | |||
292 | * separate boundary wires split by an identity (Basic) or Hadamard. | |||
293 | */ | |||
294 | struct Subdiagram { | |||
295 | // Ordered boundary edges of the subdiagram | |||
296 | std::vector<std::pair<Wire, WireEnd>> boundary_; | |||
297 | // All vertices within the subdiagram | |||
298 | ZXVertSeqSet verts_; | |||
299 | ||||
300 | Subdiagram(); | |||
301 | Subdiagram( | |||
302 | const std::vector<std::pair<Wire, WireEnd>>& cut, | |||
303 | const ZXVertSeqSet& verts); | |||
304 | ||||
305 | /** | |||
306 | * Checks for well-formedness of a subdiagram. | |||
307 | * - For each wire in `boundary`, exactly one end of it is in `verts`. | |||
308 | * - For each vertex in `verts`, every incident edge is either in `boundary` | |||
309 | * or connects to another vertex in `verts`. | |||
310 | * - There are no boundary vertices in `verts`. | |||
311 | * Throws an exception if any of these fail. | |||
312 | */ | |||
313 | void check_validity(const ZXDiagram& diag) const; | |||
314 | ||||
315 | // Copy the Subdiagram into a new ZXDiagram object. | |||
316 | ZXDiagram to_diagram(const ZXDiagram& orig) const; | |||
317 | }; | |||
318 | ||||
319 | void substitute(const ZXDiagram& to_insert, const Subdiagram& to_replace); | |||
320 | ||||
321 | friend Rewrite; | |||
322 | friend ZXDiagramPybind; | |||
323 | friend Flow; | |||
324 | ||||
325 | private: | |||
326 | /** | |||
327 | * Copy the `other`'s graph over into this diagram (such that `other.graph`) | |||
328 | * is a subgraph of `this`'s graph. | |||
329 | * Inputs: | |||
330 | * @param other the diagram to copy into `this` one | |||
331 | * @param merge_boundaries whether to update the boundary information on | |||
332 | * `this` diagram, combining the two diagrams' boundaries together. The | |||
333 | * newly added boundary elements will preserve their original order in | |||
334 | * `other`, and appended to the end of `this`'s boundary vector. | |||
335 | * Returns the isomorphism from the `other`'s graph into its copy in | |||
336 | * `this`'s graph. | |||
337 | * | |||
338 | * Users include: | |||
339 | * - the copy assignment `operator=`, | |||
340 | * - the copy constructor | |||
341 | **/ | |||
342 | std::pair<std::map<ZXVert, ZXVert>, std::map<Wire, Wire>> copy_graph( | |||
343 | const ZXDiagram& other, bool merge_boundaries = true); | |||
344 | }; | |||
345 | ||||
346 | template <typename T, typename> | |||
347 | 340 | const T& ZXDiagram::get_vertex_ZXGen(const ZXVert& v) const { | ||
348 |
1/2✓ Branch 1 taken 170 times.
✗ Branch 2 not taken.
|
340 | ZXGen_ptr pt = get_vertex_ZXGen_ptr(v); | |
349 |
1/2✓ Branch 1 taken 170 times.
✗ Branch 2 not taken.
|
680 | return dynamic_cast<const T&>(*pt); | |
350 | } | |||
351 | ||||
352 | } // namespace zx | |||
353 | ||||
354 | } // namespace tket | |||
355 |