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 <tkassert/Assert.hpp> | |||
16 | ||||
17 | #include "Utils/GraphHeaders.hpp" | |||
18 | #include "ZX/ZXDiagram.hpp" | |||
19 | ||||
20 | namespace tket { | |||
21 | ||||
22 | namespace zx { | |||
23 | ||||
24 | 1524 | ZXVert ZXDiagram::add_vertex(ZXGen_ptr op) { | ||
25 | 1524 | ZXVertProperties vp{op}; | ||
26 |
1/2✓ Branch 2 taken 1524 times.
✗ Branch 3 not taken.
|
3048 | return boost::add_vertex(vp, *graph); | |
27 | 1524 | } | ||
28 | ||||
29 | 580 | ZXVert ZXDiagram::add_vertex(ZXType type, QuantumType qtype) { | ||
30 |
1/2✓ Branch 1 taken 580 times.
✗ Branch 2 not taken.
|
580 | ZXGen_ptr op = ZXGen::create_gen(type, qtype); | |
31 |
1/2✓ Branch 2 taken 580 times.
✗ Branch 3 not taken.
|
1160 | return add_vertex(op); | |
32 | 580 | } | ||
33 | ||||
34 | 328 | ZXVert ZXDiagram::add_vertex( | ||
35 | ZXType type, const Expr& param, QuantumType qtype) { | |||
36 |
2/2✓ Branch 1 taken 327 times.
✓ Branch 2 taken 1 times.
|
328 | ZXGen_ptr op = ZXGen::create_gen(type, param, qtype); | |
37 |
1/2✓ Branch 2 taken 327 times.
✗ Branch 3 not taken.
|
654 | return add_vertex(op); | |
38 | 327 | } | ||
39 | ||||
40 | 1823 | Wire ZXDiagram::add_wire( | ||
41 | const ZXVert& va, const ZXVert& vb, const WireProperties& prop) { | |||
42 |
1/2✓ Branch 2 taken 1823 times.
✗ Branch 3 not taken.
|
1823 | auto [wire, added] = boost::add_edge(va, vb, prop, *graph); | |
43 | // add_edge only returns false if the graph cannot support parallel edges, but | |||
44 | // we have set it up to allow this | |||
45 | TKET_ASSERT(added); | |||
46 | 1823 | return wire; | ||
47 | } | |||
48 | ||||
49 | 1112 | Wire ZXDiagram::add_wire( | ||
50 | const ZXVert& va, const ZXVert& vb, ZXWireType type, QuantumType qtype, | |||
51 | std::optional<unsigned> va_port, std::optional<unsigned> vb_port) { | |||
52 |
1/2✓ Branch 2 taken 1112 times.
✗ Branch 3 not taken.
|
1112 | return add_wire(va, vb, {type, qtype, va_port, vb_port}); | |
53 | } | |||
54 | ||||
55 | 204 | void ZXDiagram::remove_vertex(const ZXVert& v) { | ||
56 | // Remove from boundary if `v` is a boundary vertex | |||
57 |
2/2✓ Branch 2 taken 97 times.
✓ Branch 3 taken 107 times.
|
2/2✓ Decision 'true' taken 97 times.
✓ Decision 'false' taken 107 times.
|
204 | if (is_boundary_type(get_zxtype(v))) { |
58 |
1/2✓ Branch 3 taken 97 times.
✗ Branch 4 not taken.
|
97 | ZXVertVec::iterator v_it = std::find(boundary.begin(), boundary.end(), v); | |
59 |
1/4✗ Branch 2 not taken.
✓ Branch 3 taken 97 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
0/1? Decision couldn't be analyzed.
|
97 | if (v_it != boundary.end()) boundary.erase(v_it); |
60 | } | |||
61 | ||||
62 | 204 | boost::clear_vertex(v, *graph); | ||
63 | 204 | boost::remove_vertex(v, *graph); | ||
64 | 204 | } | ||
65 | ||||
66 | 157 | void ZXDiagram::remove_wire(const Wire& w) { boost::remove_edge(w, *graph); } | ||
67 | ||||
68 | 2 | bool ZXDiagram::remove_wire( | ||
69 | const ZXVert& va, const ZXVert& vb, const WireProperties& prop, | |||
70 | ZXDiagram::WireSearchOption directed) { | |||
71 |
12/18✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 4 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 3 times.
✓ Branch 14 taken 1 times.
✓ Branch 16 taken 3 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 3 times.
✓ Branch 19 taken 1 times.
✓ Branch 21 taken 3 times.
✗ Branch 22 not taken.
✓ Branch 23 taken 2 times.
✓ Branch 24 taken 1 times.
|
5 | BGL_FORALL_OUTEDGES(va, w, *graph, ZXGraph) { | |
72 |
9/12✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 1 times.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 1 times.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 1 times.
✓ Branch 14 taken 2 times.
|
2/2✓ Decision 'true' taken 1 times.
✓ Decision 'false' taken 2 times.
|
3 | if ((target(w) == vb) && (get_wire_info(w) == prop)) { |
73 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | remove_wire(w); | |
74 | 1 | return true; | ||
75 | } | |||
76 | } | |||
77 | ||||
78 | // Recheck with reverse wire direction (including ports on the two ends) | |||
79 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1/2✓ Decision 'true' taken 1 times.
✗ Decision 'false' not taken.
|
1 | if (directed == WireSearchOption::UNDIRECTED) { |
80 | 1 | WireProperties rev_props = prop; | ||
81 | 1 | rev_props.source_port = prop.target_port; | ||
82 | 1 | rev_props.target_port = prop.source_port; | ||
83 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | return remove_wire(vb, va, rev_props, WireSearchOption::DIRECTED); | |
84 | } | |||
85 | ✗ | return false; | ||
86 | } | |||
87 | ||||
88 | ✗ | void ZXDiagram::symbol_substitution(const symbol_map_t& symbol_map) { | ||
89 | ✗ | SymEngine::map_basic_basic sub_map; | ||
90 |
0/2✗ Decision 'true' not taken.
✗ Decision 'false' not taken.
|
✗ | for (const std::pair<const Sym, Expr>& p : symbol_map) { | |
91 | ✗ | ExprPtr s = p.first; | ||
92 | ✗ | ExprPtr e = p.second; | ||
93 | ✗ | sub_map[s] = e; | ||
94 | } | |||
95 | ✗ | symbol_substitution(sub_map); | ||
96 | } | |||
97 | ||||
98 | ✗ | void ZXDiagram::symbol_substitution( | ||
99 | const std::map<Sym, double, SymEngine::RCPBasicKeyLess>& symbol_map) { | |||
100 | ✗ | SymEngine::map_basic_basic sub_map; | ||
101 |
0/1? Decision couldn't be analyzed.
|
✗ | for (std::pair<Sym, Expr> p : symbol_map) { | |
102 | ✗ | ExprPtr s = p.first; | ||
103 | ✗ | ExprPtr e = Expr(p.second); | ||
104 | ✗ | sub_map[s] = e; | ||
105 | } | |||
106 | ✗ | symbol_substitution(sub_map); | ||
107 | } | |||
108 | ||||
109 | 4 | void ZXDiagram::symbol_substitution(const SymEngine::map_basic_basic& sub_map) { | ||
110 | 4 | scalar = scalar.subs(sub_map); | ||
111 |
7/8✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 24 times.
✓ Branch 7 taken 4 times.
✓ Branch 9 taken 24 times.
✓ Branch 10 taken 4 times.
✓ Branch 12 taken 4 times.
✓ Branch 13 taken 4 times.
|
32 | BGL_FORALL_VERTICES(v, *graph, ZXGraph) { | |
112 |
2/4✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 24 times.
✗ Branch 6 not taken.
|
24 | ZXGen_ptr new_op = get_vertex_ZXGen_ptr(v)->symbol_substitution(sub_map); | |
113 |
3/4✓ Branch 1 taken 10 times.
✓ Branch 2 taken 14 times.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
|
1/2✓ Decision 'true' taken 24 times.
✗ Decision 'false' not taken.
|
24 | if (new_op) set_vertex_ZXGen_ptr(v, new_op); |
114 | 24 | } | ||
115 | 4 | } | ||
116 | ||||
117 | 8 | SymSet ZXDiagram::free_symbols() const { | ||
118 | 8 | SymSet symbols = expr_free_symbols(get_scalar()); | ||
119 |
7/8✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 40 times.
✓ Branch 7 taken 8 times.
✓ Branch 9 taken 40 times.
✓ Branch 10 taken 8 times.
✓ Branch 12 taken 8 times.
✓ Branch 13 taken 8 times.
|
56 | BGL_FORALL_VERTICES(v, *graph, ZXGraph) { | |
120 |
2/4✓ Branch 1 taken 40 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 40 times.
✗ Branch 6 not taken.
|
40 | const SymSet s = get_vertex_ZXGen_ptr(v)->free_symbols(); | |
121 |
1/2✓ Branch 3 taken 40 times.
✗ Branch 4 not taken.
|
40 | symbols.insert(s.begin(), s.end()); | |
122 | 40 | } | ||
123 | 8 | return symbols; | ||
124 | } | |||
125 | ||||
126 | 1 | bool ZXDiagram::is_symbolic() const { return !free_symbols().empty(); } | ||
127 | ||||
128 | 1451 | static void check_valid_wire( | ||
129 | const std::optional<unsigned>& port, QuantumType qtype, | |||
130 | const std::optional<unsigned>& n_ports, std::vector<bool>& ports_found, | |||
131 | ZXGen_ptr op) { | |||
132 |
2/2✓ Branch 1 taken 114 times.
✓ Branch 2 taken 1337 times.
|
2/2✓ Decision 'true' taken 114 times.
✓ Decision 'false' taken 1337 times.
|
1451 | if (port) { |
133 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 113 times.
|
2/2✓ Decision 'true' taken 1 times.
✓ Decision 'false' taken 113 times.
|
114 | if (!n_ports) |
134 |
2/4✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
|
1 | throw ZXError("Wire at a named port of an undirected vertex"); | |
135 |
2/2✓ Branch 3 taken 1 times.
✓ Branch 4 taken 112 times.
|
2/2✓ Decision 'true' taken 1 times.
✓ Decision 'false' taken 112 times.
|
113 | else if (ports_found.at(*port)) |
136 |
2/4✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
|
1 | throw ZXError("Multiple wires on the same port of a vertex"); | |
137 | else | |||
138 | 112 | ports_found.at(*port) = true; | ||
139 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1336 times.
|
2/2✓ Decision 'true' taken 1 times.
✓ Decision 'false' taken 1336 times.
|
1337 | } else if (n_ports) |
140 |
2/4✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
|
1 | throw ZXError("Wire at an unnamed port of a directed vertex"); | |
141 |
2/2✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1447 times.
|
2/2✓ Decision 'true' taken 1 times.
✓ Decision 'false' taken 1447 times.
|
1448 | if (!op->valid_edge(port, qtype)) |
142 |
2/4✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
|
1 | throw ZXError("QuantumType of wire is incompatible with the given port"); | |
143 | 1447 | } | ||
144 | ||||
145 | 66 | void ZXDiagram::check_validity() const { | ||
146 | 66 | std::set<ZXVert> boundary_lookup; | ||
147 |
2/2✓ Branch 5 taken 239 times.
✓ Branch 6 taken 66 times.
|
2/2✓ Decision 'true' taken 239 times.
✓ Decision 'false' taken 66 times.
|
305 | for (const ZXVert& b : boundary) { |
148 |
3/6✓ Branch 1 taken 239 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 239 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 239 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 239 times.
|
239 | if (!is_boundary_type(get_zxtype(b))) |
149 | ✗ | throw ZXError("Non-boundary vertex type in boundary"); | ||
150 |
2/4✓ Branch 1 taken 239 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 239 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 239 times.
|
239 | if (!boundary_lookup.insert(b).second) |
151 | ✗ | throw ZXError("Vertex appears in boundary multiple times"); | ||
152 | } | |||
153 |
7/8✓ Branch 2 taken 66 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 739 times.
✓ Branch 7 taken 57 times.
✓ Branch 9 taken 739 times.
✓ Branch 10 taken 57 times.
✓ Branch 12 taken 64 times.
✓ Branch 13 taken 59 times.
|
855 | BGL_FORALL_VERTICES(v, *graph, ZXGraph) { | |
154 |
1/2✓ Branch 1 taken 739 times.
✗ Branch 2 not taken.
|
739 | ZXGen_ptr op = get_vertex_ZXGen_ptr(v); | |
155 |
1/2✓ Branch 2 taken 739 times.
✗ Branch 3 not taken.
|
739 | ZXType type = op->get_type(); | |
156 |
3/4✓ Branch 1 taken 739 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 238 times.
✓ Branch 4 taken 501 times.
|
2/2✓ Decision 'true' taken 238 times.
✓ Decision 'false' taken 501 times.
|
739 | if (is_boundary_type(type)) { |
157 |
3/4✓ Branch 1 taken 238 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 236 times.
|
2/2✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 236 times.
|
238 | if (degree(v) != 1) |
158 |
2/4✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
|
2 | throw ZXError("Boundary vertex does not have degree 1"); | |
159 |
2/4✓ Branch 2 taken 236 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 236 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 236 times.
|
236 | if (boundary_lookup.find(v) == boundary_lookup.end()) |
160 | ✗ | throw ZXError("Vertex of boundary type is not in the boundary"); | ||
161 | } | |||
162 | 737 | std::optional<unsigned> n_ports = std::nullopt; | ||
163 |
3/4✓ Branch 1 taken 737 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 53 times.
✓ Branch 4 taken 684 times.
|
2/2✓ Decision 'true' taken 53 times.
✓ Decision 'false' taken 684 times.
|
737 | if (is_directed_type(type)) { |
164 | 53 | const ZXDirected& dir = static_cast<const ZXDirected&>(*op); | ||
165 |
1/2✓ Branch 1 taken 53 times.
✗ Branch 2 not taken.
|
53 | n_ports = dir.n_ports(); | |
166 | } | |||
167 |
3/4✓ Branch 2 taken 53 times.
✓ Branch 3 taken 684 times.
✓ Branch 6 taken 737 times.
✗ Branch 7 not taken.
|
737 | std::vector<bool> ports_found(n_ports ? *n_ports : 0, false); | |
168 |
11/16✓ Branch 2 taken 737 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 504 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1228 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 726 times.
✓ Branch 11 taken 502 times.
✓ Branch 13 taken 726 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 726 times.
✓ Branch 16 taken 502 times.
✓ Branch 18 taken 1239 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 504 times.
✓ Branch 21 taken 735 times.
|
1963 | BGL_FORALL_OUTEDGES(v, w, *graph, ZXGraph) { | |
169 |
5/8✓ Branch 2 taken 726 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 726 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 724 times.
✓ Branch 9 taken 2 times.
✓ Branch 12 taken 724 times.
✗ Branch 13 not taken.
|
728 | check_valid_wire(source_port(w), get_qtype(w), n_ports, ports_found, op); | |
170 | } | |||
171 |
11/16✓ Branch 2 taken 735 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 499 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1222 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 725 times.
✓ Branch 11 taken 497 times.
✓ Branch 13 taken 725 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 725 times.
✓ Branch 16 taken 497 times.
✓ Branch 18 taken 1232 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 499 times.
✓ Branch 21 taken 733 times.
|
1955 | BGL_FORALL_INEDGES(v, w, *graph, ZXGraph) { | |
172 |
5/8✓ Branch 2 taken 725 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 725 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 723 times.
✓ Branch 9 taken 2 times.
✓ Branch 12 taken 723 times.
✗ Branch 13 not taken.
|
727 | check_valid_wire(target_port(w), get_qtype(w), n_ports, ports_found, op); | |
173 | } | |||
174 |
7/8✓ Branch 0 taken 50 times.
✓ Branch 1 taken 683 times.
✓ Branch 5 taken 50 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 49 times.
✓ Branch 9 taken 1 times.
✓ Branch 10 taken 732 times.
|
2/2✓ Decision 'true' taken 1 times.
✓ Decision 'false' taken 732 times.
|
733 | if (n_ports && !std::all_of( |
175 | ports_found.begin(), ports_found.end(), | |||
176 | 839 | [](const bool& b) { return b; })) | ||
177 |
2/4✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
|
1 | throw ZXError("Not all ports of a directed vertex have wires connected"); | |
178 | 744 | } | ||
179 | 66 | } | ||
180 | ||||
181 | } // namespace zx | |||
182 | ||||
183 | } // namespace tket | |||
184 |