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 | enum class CPMDouble { Original, Conjugated }; | |||
25 | ||||
26 | struct CPMVert { | |||
27 | ZXVert vert; | |||
28 | CPMDouble conj; | |||
29 | ||||
30 | 310 | bool operator<(const CPMVert& other) const { | ||
31 |
2/2✓ Branch 0 taken 126 times.
✓ Branch 1 taken 184 times.
|
2/2✓ Decision 'true' taken 126 times.
✓ Decision 'false' taken 184 times.
|
310 | if (this->vert == other.vert) |
32 | 126 | return this->conj < other.conj; | ||
33 | else | |||
34 | 184 | return this->vert < other.vert; | ||
35 | } | |||
36 | }; | |||
37 | ||||
38 | 2 | ZXDiagram ZXDiagram::to_doubled_diagram() const { | ||
39 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | ZXDiagram doubled; | |
40 | // map from vertex | |||
41 | 2 | std::map<CPMVert, ZXVert> iso; | ||
42 | ||||
43 |
7/8✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 14 times.
✓ Branch 7 taken 2 times.
✓ Branch 9 taken 14 times.
✓ Branch 10 taken 2 times.
✓ Branch 12 taken 2 times.
✓ Branch 13 taken 2 times.
|
18 | BGL_FORALL_VERTICES(v, *this->graph, ZXGraph) { | |
44 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | ZXGen_ptr op = this->get_vertex_ZXGen_ptr(v); | |
45 |
1/2✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
|
14 | std::optional<QuantumType> qtype = op->get_qtype(); | |
46 |
3/4✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 13 times.
|
2/2✓ Decision 'true' taken 1 times.
✓ Decision 'false' taken 13 times.
|
14 | if (op->get_type() == ZXType::ZXBox) { |
47 | 1 | const ZXBox& box = static_cast<const ZXBox&>(*op); | ||
48 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
2 | ZXGen_ptr new_op = std::make_shared<const ZXBox>( | |
49 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
|
3 | box.get_diagram()->to_doubled_diagram()); | |
50 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | ZXVert added = doubled.add_vertex(new_op); | |
51 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | iso.insert({{v, CPMDouble::Original}, added}); | |
52 | 1 | } else { | ||
53 | TKET_ASSERT(qtype.has_value()); | |||
54 |
2/2✓ Branch 1 taken 10 times.
✓ Branch 2 taken 3 times.
|
2/2✓ Decision 'true' taken 10 times.
✓ Decision 'false' taken 3 times.
|
13 | if (*qtype == QuantumType::Quantum) { |
55 | 10 | ZXGen_ptr orig_op, conj_op; | ||
56 |
5/10✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
|
10 | switch (op->get_type()) { | |
57 | 6 | case ZXType::Input: | ||
58 | case ZXType::Output: | |||
59 | case ZXType::Open: { | |||
60 | 6 | orig_op = std::make_shared<const BoundaryGen>( | ||
61 |
2/4✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 6 times.
✗ Branch 6 not taken.
|
6 | op->get_type(), QuantumType::Classical); | |
62 | 6 | conj_op = orig_op; | ||
63 | 6 | break; | ||
64 | } | |||
65 | 2 | case ZXType::ZSpider: | ||
66 | case ZXType::XSpider: | |||
67 | case ZXType::XY: | |||
68 | case ZXType::YZ: { | |||
69 | 2 | const PhasedGen& bg = static_cast<const PhasedGen&>(*op); | ||
70 | 2 | orig_op = std::make_shared<const PhasedGen>( | ||
71 |
3/6✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
|
4 | op->get_type(), bg.get_param(), QuantumType::Classical); | |
72 | 2 | conj_op = std::make_shared<const PhasedGen>( | ||
73 |
4/8✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 2 times.
✗ Branch 12 not taken.
|
4 | op->get_type(), -bg.get_param(), QuantumType::Classical); | |
74 | 2 | break; | ||
75 | } | |||
76 |
0/1✗ Decision 'true' not taken.
|
✗ | case ZXType::XZ: { | |
77 | ✗ | const PhasedGen& bg = static_cast<const PhasedGen&>(*op); | ||
78 | ✗ | orig_op = std::make_shared<const PhasedGen>( | ||
79 | ✗ | op->get_type(), bg.get_param(), QuantumType::Classical); | ||
80 | ✗ | conj_op = orig_op; | ||
81 | ✗ | break; | ||
82 | } | |||
83 | ✗ | case ZXType::PX: | ||
84 | case ZXType::PZ: { | |||
85 | ✗ | const CliffordGen& bg = static_cast<const CliffordGen&>(*op); | ||
86 | ✗ | orig_op = std::make_shared<const CliffordGen>( | ||
87 | ✗ | op->get_type(), bg.get_param(), QuantumType::Classical); | ||
88 | ✗ | conj_op = orig_op; | ||
89 | ✗ | break; | ||
90 | } | |||
91 |
0/1✗ Decision 'true' not taken.
|
✗ | case ZXType::PY: { | |
92 | ✗ | const CliffordGen& bg = static_cast<const CliffordGen&>(*op); | ||
93 | ✗ | orig_op = std::make_shared<const CliffordGen>( | ||
94 | ✗ | op->get_type(), bg.get_param(), QuantumType::Classical); | ||
95 | ✗ | conj_op = std::make_shared<const CliffordGen>( | ||
96 | ✗ | op->get_type(), !bg.get_param(), QuantumType::Classical); | ||
97 | ✗ | break; | ||
98 | } | |||
99 |
1/1✓ Decision 'true' taken 1 times.
|
1 | case ZXType::Hbox: { | |
100 | 1 | const PhasedGen& bg = static_cast<const PhasedGen&>(*op); | ||
101 | 1 | orig_op = std::make_shared<const PhasedGen>( | ||
102 |
3/6✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
|
2 | op->get_type(), bg.get_param(), QuantumType::Classical); | |
103 | 1 | conj_op = std::make_shared<const PhasedGen>( | ||
104 |
3/6✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
|
2 | op->get_type(), SymEngine::conjugate(bg.get_param()), | |
105 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
2 | QuantumType::Classical); | |
106 | 1 | break; | ||
107 | } | |||
108 |
1/1✓ Decision 'true' taken 1 times.
|
1 | case ZXType::Triangle: { | |
109 | 1 | orig_op = std::make_shared<const DirectedGen>( | ||
110 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | ZXType::Triangle, QuantumType::Classical); | |
111 | 1 | conj_op = orig_op; | ||
112 | 1 | break; | ||
113 | } | |||
114 |
0/1✗ Decision 'true' not taken.
|
✗ | default: | |
115 | ✗ | throw ZXError("Unrecognised ZXType in to_doubled_diagram()"); | ||
116 | } | |||
117 |
1/2✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
|
10 | ZXVert orig = doubled.add_vertex(orig_op); | |
118 |
1/2✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
|
10 | ZXVert conj = doubled.add_vertex(conj_op); | |
119 |
1/2✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
|
10 | iso.insert({{v, CPMDouble::Original}, orig}); | |
120 |
1/2✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
|
10 | iso.insert({{v, CPMDouble::Conjugated}, conj}); | |
121 | 10 | } else { | ||
122 |
1/2✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
3 | ZXVert added = doubled.add_vertex(op); | |
123 |
1/2✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
3 | iso.insert({{v, CPMDouble::Original}, added}); | |
124 | } | |||
125 | } | |||
126 | 14 | } | ||
127 | ||||
128 |
12/18✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 9 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 8 times.
✓ Branch 14 taken 1 times.
✓ Branch 16 taken 8 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 8 times.
✓ Branch 19 taken 1 times.
✓ Branch 21 taken 3 times.
✗ Branch 22 not taken.
✓ Branch 23 taken 1 times.
✓ Branch 24 taken 2 times.
|
11 | BGL_FORALL_EDGES(w, *this->graph, ZXGraph) { | |
129 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | WireProperties wp = this->get_wire_info(w); | |
130 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | ZXVert s = source(w); | |
131 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | ZXVert t = target(w); | |
132 | 8 | std::optional<unsigned> new_s_port, new_t_port; | ||
133 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | ZXGen_ptr sgen = get_vertex_ZXGen_ptr(s); | |
134 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | ZXGen_ptr tgen = get_vertex_ZXGen_ptr(t); | |
135 | // Quantum ports on a ZXBox get mapped to two Classical ports | |||
136 |
3/4✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 7 times.
|
2/2✓ Decision 'true' taken 1 times.
✓ Decision 'false' taken 7 times.
|
8 | if (sgen->get_type() == ZXType::ZXBox) { |
137 | 1 | const ZXBox& box = static_cast<const ZXBox&>(*sgen); | ||
138 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | std::vector<QuantumType> sig = box.get_signature(); | |
139 | 1 | unsigned p = 0; | ||
140 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1 times.
|
1 | for (unsigned i = 0; i < *wp.source_port; ++i) { |
141 |
0/2✗ Decision 'true' not taken.
✗ Decision 'false' not taken.
|
✗ | if (sig.at(i) == QuantumType::Quantum) | |
142 | ✗ | p += 2; | ||
143 | else | |||
144 | ✗ | p += 1; | ||
145 | } | |||
146 | 1 | new_s_port = p; | ||
147 | 1 | } | ||
148 | // Other generators just get duplicated | |||
149 | else { | |||
150 | 7 | new_s_port = wp.source_port; | ||
151 | } | |||
152 | // Do the same for the target | |||
153 |
3/4✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 7 times.
|
2/2✓ Decision 'true' taken 1 times.
✓ Decision 'false' taken 7 times.
|
8 | if (tgen->get_type() == ZXType::ZXBox) { |
154 | 1 | const ZXBox& box = static_cast<const ZXBox&>(*tgen); | ||
155 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | std::vector<QuantumType> sig = box.get_signature(); | |
156 | 1 | unsigned p = 0; | ||
157 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
|
2/2✓ Decision 'true' taken 1 times.
✓ Decision 'false' taken 1 times.
|
2 | for (unsigned i = 0; i < *wp.target_port; ++i) { |
158 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
|
1/2✓ Decision 'true' taken 1 times.
✗ Decision 'false' not taken.
|
1 | if (sig.at(i) == QuantumType::Quantum) |
159 | 1 | p += 2; | ||
160 | else | |||
161 | ✗ | p += 1; | ||
162 | } | |||
163 | 1 | new_t_port = p; | ||
164 | 1 | } else { | ||
165 | 7 | new_t_port = wp.target_port; | ||
166 | } | |||
167 | WireProperties new_wp{ | |||
168 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | wp.type, QuantumType::Classical, new_s_port, new_t_port}; | |
169 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | ZXVert new_s = iso.at({s, CPMDouble::Original}); | |
170 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | ZXVert new_t = iso.at({t, CPMDouble::Original}); | |
171 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | doubled.add_wire(new_s, new_t, new_wp); | |
172 | // Handle the second wire for expanding Quantum wires | |||
173 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
|
2/2✓ Decision 'true' taken 6 times.
✓ Decision 'false' taken 2 times.
|
8 | if (wp.qtype == QuantumType::Quantum) { |
174 | // For ZXBox, the conjugated port is the next one | |||
175 |
3/4✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 5 times.
|
2/2✓ Decision 'true' taken 5 times.
✓ Decision 'false' taken 1 times.
|
6 | if (sgen->get_type() == ZXType::ZXBox) *new_wp.source_port += 1; |
176 | // For other generators, the conjugate is a new vertex | |||
177 |
2/4✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 5 times.
✗ Branch 6 not taken.
|
1/2✓ Decision 'true' taken 5 times.
✗ Decision 'false' not taken.
|
5 | else if (sgen->get_qtype() == QuantumType::Quantum) |
178 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
5 | new_s = iso.at({s, CPMDouble::Conjugated}); | |
179 | // Do the same for the target | |||
180 |
3/4✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 5 times.
|
2/2✓ Decision 'true' taken 1 times.
✓ Decision 'false' taken 5 times.
|
6 | if (tgen->get_type() == ZXType::ZXBox) |
181 | 1 | *new_wp.target_port += 1; | ||
182 |
3/4✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 1 times.
|
2/2✓ Decision 'true' taken 4 times.
✓ Decision 'false' taken 1 times.
|
5 | else if (tgen->get_qtype() == QuantumType::Quantum) |
183 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | new_t = iso.at({t, CPMDouble::Conjugated}); | |
184 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | doubled.add_wire(new_s, new_t, new_wp); | |
185 | } | |||
186 | 8 | } | ||
187 | ||||
188 |
2/2✓ Branch 5 taken 8 times.
✓ Branch 6 taken 2 times.
|
2/2✓ Decision 'true' taken 8 times.
✓ Decision 'false' taken 2 times.
|
10 | for (const ZXVert& b : boundary) { |
189 |
2/4✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
|
8 | doubled.boundary.push_back(iso.at({b, CPMDouble::Original})); | |
190 |
3/4✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 2 times.
|
2/2✓ Decision 'true' taken 6 times.
✓ Decision 'false' taken 2 times.
|
8 | if (get_qtype(b) == QuantumType::Quantum) |
191 |
2/4✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
|
6 | doubled.boundary.push_back(iso.at({b, CPMDouble::Conjugated})); | |
192 | } | |||
193 | ||||
194 | 4 | return doubled; | ||
195 | 2 | } | ||
196 | ||||
197 | 1 | ZXDiagram ZXDiagram::to_quantum_embedding() const { | ||
198 | 1 | ZXDiagram embedding(*this); | ||
199 |
2/2✓ Branch 5 taken 6 times.
✓ Branch 6 taken 1 times.
|
2/2✓ Decision 'true' taken 6 times.
✓ Decision 'false' taken 1 times.
|
7 | for (ZXVert& b : embedding.boundary) { |
200 |
3/4✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
|
2/2✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 4 times.
|
6 | if (embedding.get_qtype(b) == QuantumType::Classical) { |
201 | ZXVert new_b = | |||
202 |
2/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
2 | embedding.add_vertex(embedding.get_zxtype(b), QuantumType::Quantum); | |
203 | 4 | ZXGen_ptr id = std::make_shared<const PhasedGen>( | ||
204 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | ZXType::ZSpider, 0., QuantumType::Classical); | |
205 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | embedding.set_vertex_ZXGen_ptr(b, id); | |
206 |
1/2✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
|
2 | embedding.add_wire(new_b, b, ZXWireType::Basic, QuantumType::Quantum); | |
207 | 2 | b = new_b; | ||
208 | 2 | } | ||
209 | } | |||
210 | ||||
211 | 1 | return embedding; | ||
212 | } | |||
213 | ||||
214 | } // namespace zx | |||
215 | ||||
216 | } // namespace tket | |||
217 |