GCC Code Coverage Report


Directory: ./
File: Converters/ZXConverters.cpp
Date: 2022-10-15 05:10:18
Warnings: 11 unchecked decisions!
Exec Total Coverage
Lines: 567 622 91.2%
Functions: 12 12 100.0%
Branches: 649 1335 48.6%
Decisions: 121 167 72.5%

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 "Circuit/CircPool.hpp"
16 #include "Converters.hpp"
17 #include "ZX/Flow.hpp"
18 #include "ZX/ZXDiagram.hpp"
19
20 namespace tket {
21
22 using namespace zx;
23
24 enum class ZXPortType { In, Out };
25 typedef std::pair<VertPort, ZXPortType> TypedVertPort;
26 typedef std::pair<ZXVert, std::optional<unsigned>> ZXVertPort;
27 typedef std::vector<ZXVertPort> ZXVertPortVec;
28 typedef boost::bimap<ZXVert, Vertex> BoundaryVertMap;
29
30 366 bool is_spiderless_optype(const OpType& optype) {
31
4/4
✓ Branch 0 taken 338 times.
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 314 times.
✓ Branch 3 taken 24 times.
680 return optype == OpType::Barrier || optype == OpType::SWAP ||
32
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 306 times.
680 optype == OpType::noop;
33 }
34
35 // Add a swicth with the on-state controlled by the on_value.
36 // qtype indicates the quantum type of the switch.
37 32 std::pair<ZXVertPort, ZXVertPort> add_switch(
38 ZXDiagram& zxd, const bool& on_value, const QuantumType& qtype) {
39
2/4
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 32 times.
✗ Branch 5 not taken.
32 zxd.multiply_scalar(std::sqrt(2.));
40
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 ZXVert triangle = zxd.add_vertex(ZXType::Triangle, qtype);
41
2/4
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 32 times.
✗ Branch 5 not taken.
32 ZXVert x = zxd.add_vertex(ZXType::XSpider, 0, qtype);
42
1/2
✓ Branch 3 taken 32 times.
✗ Branch 4 not taken.
32 zxd.add_wire(triangle, x, ZXWireType::Basic, qtype, 1);
43
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 16 times.
2/2
✓ Decision 'true' taken 16 times.
✓ Decision 'false' taken 16 times.
32 if (on_value) {
44 // Turn on the switch if the input is 1
45
2/4
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
16 ZXVert negate = zxd.add_vertex(ZXType::XSpider, 1, qtype);
46
1/2
✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
16 zxd.add_wire(triangle, negate, ZXWireType::Basic, qtype, 0);
47 16 return {{negate, std::nullopt}, {x, std::nullopt}};
48 }
49 16 return {{triangle, 0}, {x, std::nullopt}};
50 }
51
52 // --o--s---o--
53 // | |
54 // n n
55 // |--G-o-|
56 // |
57 // s
58 // |-discard
59 // s, n are switches, G is the conditional zx diagram specified by 'left' and
60 // 'right'. s is on when the input is 0, n is on when the input is 1.
61 // return the input and the output of the conditional zx along with a vector of
62 // control spiders
63 8 std::pair<std::pair<ZXVertPort, ZXVertPort>, ZXVertPortVec> add_conditional_zx(
64 ZXDiagram& zxd, const ZXVert& left, const ZXVert& right,
65 const QuantumType& qtype) {
66
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 ZXVert in = zxd.add_vertex(ZXType::ZSpider, 0, qtype);
67
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 ZXVert out = zxd.add_vertex(ZXType::ZSpider, 0, qtype);
68
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 ZXVert discard_ctr = zxd.add_vertex(ZXType::ZSpider, 0, qtype);
69
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 ZXVert discard = zxd.add_vertex(ZXType::ZSpider, 0, QuantumType::Classical);
70
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 auto switch_s0 = add_switch(zxd, false, qtype);
71
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 auto switch_n0 = add_switch(zxd, true, qtype);
72
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 auto switch_n1 = add_switch(zxd, true, qtype);
73
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 auto switch_s1 = add_switch(zxd, false, qtype);
74
1/2
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
8 zxd.add_wire(in, switch_s0.second.first, ZXWireType::Basic, qtype);
75
1/2
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
8 zxd.add_wire(out, switch_s0.second.first, ZXWireType::Basic, qtype);
76
1/2
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
8 zxd.add_wire(in, switch_n0.second.first, ZXWireType::Basic, qtype);
77
1/2
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
8 zxd.add_wire(left, switch_n0.second.first, ZXWireType::Basic, qtype);
78
1/2
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
8 zxd.add_wire(right, discard_ctr, ZXWireType::Basic, qtype);
79
1/2
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
8 zxd.add_wire(discard_ctr, switch_n1.second.first, ZXWireType::Basic, qtype);
80
1/2
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
8 zxd.add_wire(out, switch_n1.second.first, ZXWireType::Basic, qtype);
81
1/2
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
8 zxd.add_wire(discard_ctr, switch_s1.second.first, ZXWireType::Basic, qtype);
82
1/2
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
8 zxd.add_wire(discard, switch_s1.second.first, ZXWireType::Basic, qtype);
83 return {
84 {{in, std::nullopt}, {out, std::nullopt}},
85
2/4
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
8 {switch_s0.first, switch_s1.first, switch_n0.first, switch_n1.first}};
86 }
87
88 // n-bit AND spider to control the switches
89 // https://arxiv.org/abs/1910.06818
90 2 std::pair<ZXVertPortVec, ZXVertPort> add_n_bit_and(
91 ZXDiagram& zxd, unsigned n, const QuantumType& qtype) {
92 TKET_ASSERT(n > 1);
93
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 ZXVert z_vert = zxd.add_vertex(ZXType::ZSpider, 0, qtype);
94 // Add Triangle -1
95
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 ZXVert z_pi_0 = zxd.add_vertex(ZXType::ZSpider, 1, qtype);
96
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 ZXVert tri_1 = zxd.add_vertex(ZXType::Triangle, qtype);
97
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 ZXVert z_pi_1 = zxd.add_vertex(ZXType::ZSpider, 1, qtype);
98
1/2
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 zxd.add_wire(z_vert, z_pi_0, ZXWireType::Basic, qtype);
99
1/2
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 zxd.add_wire(tri_1, z_pi_0, ZXWireType::Basic, qtype, 0);
100
1/2
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 zxd.add_wire(tri_1, z_pi_1, ZXWireType::Basic, qtype, 1);
101 2 ZXVertPortVec inputs;
102
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2 times.
2/2
✓ Decision 'true' taken 5 times.
✓ Decision 'false' taken 2 times.
7 for (unsigned i = 0; i < n; i++) {
103
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 ZXVert tri_0 = zxd.add_vertex(ZXType::Triangle, qtype);
104
1/2
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
5 zxd.add_wire(tri_0, z_vert, ZXWireType::Basic, qtype, 1);
105
1/2
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 inputs.push_back({tri_0, 0});
106 }
107
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 return {inputs, {z_pi_1, std::nullopt}};
108 2 }
109
110 // Add converted circ into zxd. Set add_boundary to true
111 // if boundary spiders are to be added to the boundary.
112 38 BoundaryVertMap circuit_to_zx_recursive(
113 const Circuit& circ, ZXDiagram& zxd, bool add_boundary) {
114 38 std::map<TypedVertPort, ZXVertPort> vert_lookup;
115 38 std::map<VertPort, ZXVertPort> boolean_outport_lookup;
116
1/2
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 BoundaryVertMap bmap;
117
118 // Convert each vertex to ZXDiagram, raise error if not supported
119
7/8
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 221 times.
✓ Branch 6 taken 37 times.
✓ Branch 8 taken 221 times.
✓ Branch 9 taken 37 times.
✓ Branch 11 taken 37 times.
✓ Branch 12 taken 38 times.
296 BGL_FORALL_VERTICES(vert, circ.dag, DAG) {
120 // We currently throw an error if the vertex is either classical or flow
121
1/2
✓ Branch 1 taken 221 times.
✗ Branch 2 not taken.
221 Op_ptr op = circ.get_Op_ptr_from_Vertex(vert);
122
5/10
✓ Branch 3 taken 221 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 221 times.
✗ Branch 6 not taken.
✓ Branch 10 taken 221 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 221 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 221 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 221 times.
221 if (is_flowop_type(op->get_type()) || is_classical_type(op->get_type())) {
123 throw Unsupported(
124 "Cannot convert OpType: " + op->get_name() + " to a ZX node. \n");
125 }
126
19/19
✓ Branch 2 taken 62 times.
✓ Branch 3 taken 62 times.
✓ Branch 4 taken 15 times.
✓ Branch 5 taken 15 times.
✓ Branch 6 taken 17 times.
✓ Branch 7 taken 9 times.
✓ Branch 8 taken 7 times.
✓ Branch 9 taken 2 times.
✓ Branch 10 taken 5 times.
✓ Branch 11 taken 1 times.
✓ Branch 12 taken 4 times.
✓ Branch 13 taken 3 times.
✓ Branch 14 taken 4 times.
✓ Branch 15 taken 2 times.
✓ Branch 16 taken 2 times.
✓ Branch 17 taken 1 times.
✓ Branch 18 taken 1 times.
✓ Branch 19 taken 6 times.
✓ Branch 20 taken 3 times.
221 switch (op->get_type()) {
127
1/1
✓ Decision 'true' taken 62 times.
62 case OpType::Input: {
128
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 ZXVert zx_vert = zxd.add_vertex(ZXType::Input, QuantumType::Quantum);
129
3/4
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 15 times.
✓ Branch 3 taken 47 times.
✗ Branch 4 not taken.
1/2
✓ Decision 'true' taken 62 times.
✗ Decision 'false' not taken.
62 if (add_boundary) zxd.add_boundary(zx_vert);
130
1/2
✓ Branch 3 taken 62 times.
✗ Branch 4 not taken.
62 vert_lookup.insert(
131 62 {{{vert, 0}, ZXPortType::Out}, {zx_vert, std::nullopt}});
132
2/4
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 62 times.
✗ Branch 5 not taken.
62 bmap.insert({zx_vert, vert});
133 62 break;
134 }
135
1/1
✓ Decision 'true' taken 62 times.
62 case OpType::Output: {
136
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 ZXVert zx_vert = zxd.add_vertex(ZXType::Output, QuantumType::Quantum);
137
3/4
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 15 times.
✓ Branch 3 taken 47 times.
✗ Branch 4 not taken.
1/2
✓ Decision 'true' taken 62 times.
✗ Decision 'false' not taken.
62 if (add_boundary) zxd.add_boundary(zx_vert);
138
1/2
✓ Branch 3 taken 62 times.
✗ Branch 4 not taken.
62 vert_lookup.insert(
139 62 {{{vert, 0}, ZXPortType::In}, {zx_vert, std::nullopt}});
140
2/4
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 62 times.
✗ Branch 5 not taken.
62 bmap.insert({zx_vert, vert});
141 62 break;
142 }
143
1/1
✓ Decision 'true' taken 15 times.
15 case OpType::ClInput: {
144
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
15 ZXVert zx_vert = zxd.add_vertex(ZXType::Input, QuantumType::Classical);
145
3/4
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 1 times.
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
1/2
✓ Decision 'true' taken 15 times.
✗ Decision 'false' not taken.
15 if (add_boundary) zxd.add_boundary(zx_vert);
146
1/2
✓ Branch 3 taken 15 times.
✗ Branch 4 not taken.
15 vert_lookup.insert(
147 15 {{{vert, 0}, ZXPortType::Out}, {zx_vert, std::nullopt}});
148
2/4
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
15 bmap.insert({zx_vert, vert});
149 15 break;
150 }
151
1/1
✓ Decision 'true' taken 15 times.
15 case OpType::ClOutput: {
152
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
15 ZXVert zx_vert = zxd.add_vertex(ZXType::Output, QuantumType::Classical);
153
3/4
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 1 times.
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
1/2
✓ Decision 'true' taken 15 times.
✗ Decision 'false' not taken.
15 if (add_boundary) zxd.add_boundary(zx_vert);
154
1/2
✓ Branch 3 taken 15 times.
✗ Branch 4 not taken.
15 vert_lookup.insert(
155 15 {{{vert, 0}, ZXPortType::In}, {zx_vert, std::nullopt}});
156
2/4
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
15 bmap.insert({zx_vert, vert});
157 15 break;
158 }
159 // Spiderless ops are handled during vertex wiring
160 17 case OpType::Barrier:
161 case OpType::noop:
162 case OpType::SWAP: {
163 17 continue;
164 }
165
1/1
✓ Decision 'true' taken 9 times.
9 case OpType::H: {
166
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 ZXVert zx_vert = zxd.add_vertex(ZXType::Hbox, QuantumType::Quantum);
167
1/2
✓ Branch 3 taken 9 times.
✗ Branch 4 not taken.
9 vert_lookup.insert(
168 9 {{{vert, 0}, ZXPortType::In}, {zx_vert, std::nullopt}});
169
1/2
✓ Branch 3 taken 9 times.
✗ Branch 4 not taken.
9 vert_lookup.insert(
170 9 {{{vert, 0}, ZXPortType::Out}, {zx_vert, std::nullopt}});
171
2/4
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
9 zxd.multiply_scalar(0.5);
172 9 break;
173 }
174
1/1
✓ Decision 'true' taken 7 times.
7 case OpType::Rz: {
175
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 ZXVert zx_vert = zxd.add_vertex(
176
1/2
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
14 ZXType::ZSpider, op->get_params()[0], QuantumType::Quantum);
177
1/2
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
7 vert_lookup.insert(
178 7 {{{vert, 0}, ZXPortType::In}, {zx_vert, std::nullopt}});
179
1/2
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
7 vert_lookup.insert(
180 7 {{{vert, 0}, ZXPortType::Out}, {zx_vert, std::nullopt}});
181 7 break;
182 }
183
1/1
✓ Decision 'true' taken 2 times.
2 case OpType::Rx: {
184
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 ZXVert zx_vert = zxd.add_vertex(
185
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 ZXType::XSpider, op->get_params()[0], QuantumType::Quantum);
186
1/2
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 vert_lookup.insert(
187 2 {{{vert, 0}, ZXPortType::In}, {zx_vert, std::nullopt}});
188
1/2
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 vert_lookup.insert(
189 2 {{{vert, 0}, ZXPortType::Out}, {zx_vert, std::nullopt}});
190 2 break;
191 }
192 5 case OpType::X: {
193 ZXVert zx_vert =
194
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
5 zxd.add_vertex(ZXType::XSpider, 1, QuantumType::Quantum);
195
1/2
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
5 vert_lookup.insert(
196 5 {{{vert, 0}, ZXPortType::In}, {zx_vert, std::nullopt}});
197
1/2
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
5 vert_lookup.insert(
198 5 {{{vert, 0}, ZXPortType::Out}, {zx_vert, std::nullopt}});
199 5 break;
200 }
201 1 case OpType::Z: {
202 ZXVert zx_vert =
203
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 zxd.add_vertex(ZXType::ZSpider, 1, QuantumType::Quantum);
204
1/2
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
1 vert_lookup.insert(
205 1 {{{vert, 0}, ZXPortType::In}, {zx_vert, std::nullopt}});
206
1/2
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
1 vert_lookup.insert(
207 1 {{{vert, 0}, ZXPortType::Out}, {zx_vert, std::nullopt}});
208 1 break;
209 }
210 4 case OpType::CX: {
211 ZXVert zx_x_vert =
212
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 zxd.add_vertex(ZXType::XSpider, 0, QuantumType::Quantum);
213 ZXVert zx_z_vert =
214
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 zxd.add_vertex(ZXType::ZSpider, 0, QuantumType::Quantum);
215
1/2
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
4 zxd.add_wire(zx_x_vert, zx_z_vert);
216
1/2
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
4 vert_lookup.insert(
217 4 {{{vert, 0}, ZXPortType::In}, {zx_z_vert, std::nullopt}});
218
1/2
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
4 vert_lookup.insert(
219 4 {{{vert, 0}, ZXPortType::Out}, {zx_z_vert, std::nullopt}});
220
1/2
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
4 vert_lookup.insert(
221 4 {{{vert, 1}, ZXPortType::In}, {zx_x_vert, std::nullopt}});
222
1/2
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
4 vert_lookup.insert(
223 4 {{{vert, 1}, ZXPortType::Out}, {zx_x_vert, std::nullopt}});
224
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 zxd.multiply_scalar(2);
225 4 break;
226 }
227 3 case OpType::CZ: {
228 ZXVert zx_za_vert =
229
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 zxd.add_vertex(ZXType::ZSpider, 0, QuantumType::Quantum);
230 ZXVert zx_zb_vert =
231
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 zxd.add_vertex(ZXType::ZSpider, 0, QuantumType::Quantum);
232
1/2
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
3 zxd.add_wire(zx_za_vert, zx_zb_vert, ZXWireType::H);
233
1/2
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
3 vert_lookup.insert(
234 3 {{{vert, 0}, ZXPortType::In}, {zx_za_vert, std::nullopt}});
235
1/2
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
3 vert_lookup.insert(
236 3 {{{vert, 0}, ZXPortType::Out}, {zx_za_vert, std::nullopt}});
237
1/2
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
3 vert_lookup.insert(
238 3 {{{vert, 1}, ZXPortType::In}, {zx_zb_vert, std::nullopt}});
239
1/2
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
3 vert_lookup.insert(
240 3 {{{vert, 1}, ZXPortType::Out}, {zx_zb_vert, std::nullopt}});
241 3 break;
242 }
243
0/1
✗ Decision 'true' not taken.
4 case OpType::Measure: {
244 // Add a decoherence node
245 ZXVert zx_measure_vert =
246
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 zxd.add_vertex(ZXType::ZSpider, 0, QuantumType::Classical);
247 // Add a delete operator
248 ZXVert zx_delete_vert =
249
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 zxd.add_vertex(ZXType::ZSpider, 0, QuantumType::Classical);
250
1/2
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
4 vert_lookup.insert(
251 4 {{{vert, 0}, ZXPortType::In}, {zx_measure_vert, std::nullopt}});
252
1/2
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
4 vert_lookup.insert(
253 4 {{{vert, 0}, ZXPortType::Out}, {zx_measure_vert, std::nullopt}});
254
1/2
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
4 vert_lookup.insert(
255 4 {{{vert, 1}, ZXPortType::In}, {zx_delete_vert, std::nullopt}});
256
1/2
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
4 vert_lookup.insert(
257 4 {{{vert, 1}, ZXPortType::Out}, {zx_measure_vert, std::nullopt}});
258 4 break;
259 }
260
0/1
✗ Decision 'true' not taken.
2 case OpType::Reset: {
261 // Discard
262 ZXVert zx_discard_vert =
263
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 zxd.add_vertex(ZXType::ZSpider, 0, QuantumType::Classical);
264 // Add a node to prepare |0>
265 ZXVert zx_reset_vert =
266
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 zxd.add_vertex(ZXType::XSpider, 0, QuantumType::Quantum);
267
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 zxd.multiply_scalar(0.5);
268
1/2
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 vert_lookup.insert(
269 2 {{{vert, 0}, ZXPortType::In}, {zx_discard_vert, std::nullopt}});
270
1/2
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 vert_lookup.insert(
271 2 {{{vert, 0}, ZXPortType::Out}, {zx_reset_vert, std::nullopt}});
272 2 break;
273 }
274 2 case OpType::Collapse: {
275 ZXVert zx_vert =
276
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 zxd.add_vertex(ZXType::ZSpider, QuantumType::Classical);
277
1/2
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 vert_lookup.insert(
278 2 {{{vert, 0}, ZXPortType::In}, {zx_vert, std::nullopt}});
279
1/2
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 vert_lookup.insert(
280 2 {{{vert, 0}, ZXPortType::Out}, {zx_vert, std::nullopt}});
281 2 break;
282 }
283 1 case OpType::Create: {
284 ZXVert zx_init_vert =
285
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 zxd.add_vertex(ZXType::XSpider, 0, QuantumType::Quantum);
286
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 zxd.multiply_scalar(0.5);
287
1/2
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
1 vert_lookup.insert(
288 1 {{{vert, 0}, ZXPortType::Out}, {zx_init_vert, std::nullopt}});
289 1 break;
290 }
291 1 case OpType::Discard: {
292 ZXVert zx_discard_vert =
293
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 zxd.add_vertex(ZXType::ZSpider, 0, QuantumType::Classical);
294
1/2
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
1 vert_lookup.insert(
295 1 {{{vert, 0}, ZXPortType::In}, {zx_discard_vert, std::nullopt}});
296 1 break;
297 }
298
0/1
✗ Decision 'true' not taken.
6 case OpType::Conditional: {
299 // Stores the condition for each port index
300 6 std::vector<bool> port_conditions;
301
302
1/2
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 op_signature_t parent_sig = op->get_signature();
303
304 // Find the nested quantum op and its overall conditions
305 // Assume bool ports are always the first few ports
306
2/2
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 6 times.
2/2
✓ Decision 'true' taken 7 times.
✓ Decision 'false' taken 6 times.
13 while (op->get_type() == OpType::Conditional) {
307 7 const Conditional& cond = static_cast<const Conditional&>(*op);
308
3/4
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
✓ Branch 4 taken 7 times.
0/1
? Decision couldn't be analyzed.
16 for (unsigned i = 0; i < cond.get_width(); i++) {
309
2/4
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
9 bool set = cond.get_value() & (1 << (cond.get_width() - i - 1));
310
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 port_conditions.push_back(set);
311 }
312
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 op = cond.get_op();
313 }
314 unsigned port_conditions_size =
315 6 static_cast<unsigned>(port_conditions.size());
316 // Convert the underlying op to zx.
317 // If the op is a quantum gate, construct a 1 gate circuit
318 // If the op is a box, obtain its circuit decomposition
319
1/2
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 op_signature_t inner_sig = op->get_signature();
320
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 Circuit replacement;
321
3/4
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 5 times.
2/2
✓ Decision 'true' taken 1 times.
✓ Decision 'false' taken 5 times.
6 if (is_box_type(op->get_type())) {
322 1 const Box& b = static_cast<const Box&>(*op);
323
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
1 replacement = *b.to_circuit();
324 } else {
325 5 unit_vector_t args;
326 5 unsigned q_index = 0;
327 5 unsigned c_index = 0;
328
2/2
✓ Branch 5 taken 6 times.
✓ Branch 6 taken 5 times.
2/2
✓ Decision 'true' taken 6 times.
✓ Decision 'false' taken 5 times.
11 for (const EdgeType& etype : inner_sig) {
329
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
2/2
✓ Decision 'true' taken 5 times.
✓ Decision 'false' taken 1 times.
6 if (etype == EdgeType::Quantum) {
330
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
5 args.push_back(Qubit(q_index++));
331
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1/2
✓ Decision 'true' taken 1 times.
✗ Decision 'false' not taken.
1 } else if (etype == EdgeType::Classical) {
332
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 args.push_back(Bit(c_index++));
333 }
334 TKET_ASSERT(etype != EdgeType::Boolean);
335 }
336
2/4
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 5 times.
✗ Branch 6 not taken.
5 replacement = Circuit(q_index, c_index);
337
1/2
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 replacement.add_op(op, args);
338 5 }
339 BoundaryVertMap box_bm =
340
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 circuit_to_zx_recursive(replacement, zxd, false);
341
342 // The Z spider controlling all switches
343 ZXVert master_switch =
344
2/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
6 zxd.add_vertex(ZXType::ZSpider, 0, QuantumType::Classical);
345
346 // For each qubit/bit path in the inner op, convert it into a
347 // conditional path.
348 6 unsigned q_index = 0;
349 6 unsigned c_index = 0;
350
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 6 times.
2/2
✓ Decision 'true' taken 8 times.
✓ Decision 'false' taken 6 times.
14 for (unsigned i = 0; i < inner_sig.size(); i++) {
351 QuantumType qtype;
352
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 UnitID reg;
353
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 1 times.
2/2
✓ Decision 'true' taken 7 times.
✓ Decision 'false' taken 1 times.
8 if (inner_sig[i] == EdgeType::Quantum) {
354 7 qtype = QuantumType::Quantum;
355
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 reg = Qubit(q_index++);
356 } else {
357 1 qtype = QuantumType::Classical;
358
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 reg = Bit(c_index++);
359 }
360 8 std::pair<ZXVertPort, ZXVertPort> boundary;
361 8 ZXVertPortVec controls;
362
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 Vertex inp = replacement.get_in(reg);
363
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 ZXVert zx_inp = box_bm.right.at(inp);
364
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 Vertex outp = replacement.get_out(reg);
365
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 ZXVert zx_outp = box_bm.right.at(outp);
366 // Convert the path between zx_inp and zx_outp into a conditional
367 // path
368 8 std::tie(boundary, controls) =
369
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
16 add_conditional_zx(zxd, zx_inp, zx_outp, qtype);
370 // Connect each switch to the master switch
371
2/2
✓ Branch 4 taken 32 times.
✓ Branch 5 taken 8 times.
2/2
✓ Decision 'true' taken 32 times.
✓ Decision 'false' taken 8 times.
40 for (const ZXVertPort& ctr : controls) {
372 32 zxd.add_wire(
373 32 ctr.first, master_switch, ZXWireType::Basic,
374
3/6
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 32 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 32 times.
✗ Branch 8 not taken.
32 zxd.get_qtype(ctr.first).value(), ctr.second);
375 }
376 // Update lookup
377 TKET_ASSERT(parent_sig[i + port_conditions_size] == inner_sig[i]);
378 // Since we assume that the conditions always use the first few
379 // ports, the gate path should use the i + port_conditions.size()
380 // port.
381
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 vert_lookup.insert(
382 8 {{{vert, i + port_conditions_size}, ZXPortType::In},
383 boundary.first});
384
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 vert_lookup.insert(
385 8 {{{vert, i + port_conditions_size}, ZXPortType::Out},
386 boundary.second});
387 8 }
388 // Use either a Z spider or a AND operator to connect the master
389 // switch and the boolean inputs
390 6 ZXVertPortVec and_inputs;
391
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
2/2
✓ Decision 'true' taken 4 times.
✓ Decision 'false' taken 2 times.
6 if (port_conditions_size == 1) {
392
1/2
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 and_inputs.push_back({master_switch, std::nullopt});
393 } else {
394 2 ZXVertPort and_output;
395 2 std::tie(and_inputs, and_output) =
396
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 add_n_bit_and(zxd, port_conditions_size, QuantumType::Classical);
397
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 zxd.add_wire(
398 and_output.first, master_switch, ZXWireType::Basic,
399 QuantumType::Classical, and_output.second);
400 }
401 // Connect inputs to the nodes obtained from above
402
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 6 times.
2/2
✓ Decision 'true' taken 9 times.
✓ Decision 'false' taken 6 times.
15 for (unsigned i = 0; i < port_conditions_size; i++) {
403 // Each boolean edge shares a source port with other
404 // classical/boolean edges. Use the classical Z spider to explicitly
405 // implement this copy operation
406
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 Edge in_edge = circ.get_nth_in_edge(vert, i);
407
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 port_t source_port = circ.get_source_port(in_edge);
408
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 Vertex vert_s = circ.source(in_edge);
409 // During the wiring stage, each edge originated from {vert_s,
410 // p_s} needs go through copy_vert
411
1/2
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
2/2
✓ Decision 'true' taken 8 times.
✓ Decision 'false' taken 1 times.
9 if (boolean_outport_lookup.find({vert_s, source_port}) ==
412
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 1 times.
18 boolean_outport_lookup.end()) {
413 ZXVert copy_vert =
414
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 zxd.add_vertex(ZXType::ZSpider, 0, QuantumType::Classical);
415
1/2
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 boolean_outport_lookup.insert(
416 {{vert_s, source_port}, {copy_vert, std::nullopt}});
417 }
418
3/4
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✓ Branch 5 taken 2 times.
2/2
✓ Decision 'true' taken 7 times.
✓ Decision 'false' taken 2 times.
9 if (port_conditions[i]) {
419
1/2
✓ Branch 5 taken 7 times.
✗ Branch 6 not taken.
7 vert_lookup.insert({{{vert, i}, ZXPortType::In}, and_inputs[i]});
420 } else {
421 ZXVert x_vert =
422
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 zxd.add_vertex(ZXType::XSpider, 1, QuantumType::Classical);
423 2 zxd.add_wire(
424
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 and_inputs[i].first, x_vert, ZXWireType::Basic,
425 2 QuantumType::Classical, and_inputs[i].second);
426
1/2
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
4 vert_lookup.insert(
427 4 {{{vert, i}, ZXPortType::In}, {x_vert, std::nullopt}});
428 }
429 }
430 6 break;
431 6 }
432
1/1
✓ Decision 'true' taken 3 times.
3 default:
433
2/4
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
1/2
✓ Decision 'true' taken 3 times.
✗ Decision 'false' not taken.
3 if (is_box_type(op->get_type())) {
434 EdgeVec b_in_holes =
435
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 circ.get_in_edges_of_type(vert, EdgeType::Boolean);
436
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 3 times.
3 if (b_in_holes.size() > 0) {
437 throw Unsupported("Cannot convert box type: " + op->get_name());
438 }
439 3 const Box& b = static_cast<const Box&>(*op);
440
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
3 Circuit replacement = *b.to_circuit();
441 // First, add the converted box to diagram.
442 BoundaryVertMap box_bm =
443
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 circuit_to_zx_recursive(replacement, zxd, false);
444 // Then, map the vertports in the box boundary to zx nodes.
445 // Assume that a box can't have Boolean input edges, and all Boolean
446 // output edges share ports with Classical edges. Therefore we don't
447 // have to map Boolean vertports.
448 EdgeVec q_in_holes =
449
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 circ.get_in_edges_of_type(vert, EdgeType::Quantum);
450 EdgeVec q_out_holes =
451
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 circ.get_out_edges_of_type(vert, EdgeType::Quantum);
452 EdgeVec c_in_holes =
453
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 circ.get_in_edges_of_type(vert, EdgeType::Classical);
454 EdgeVec c_out_holes =
455
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 circ.get_out_edges_of_type(vert, EdgeType::Classical);
456
457
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 3 times.
2/2
✓ Decision 'true' taken 8 times.
✓ Decision 'false' taken 3 times.
11 for (unsigned i = 0; i < q_in_holes.size(); i++) {
458
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 port_t port = circ.get_target_port(q_in_holes[i]);
459
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 Vertex inp = replacement.get_in(Qubit(i));
460
1/2
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
16 vert_lookup.insert(
461 16 {{{vert, port}, ZXPortType::In},
462
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 {box_bm.right.at(inp), std::nullopt}});
463 }
464
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 3 times.
2/2
✓ Decision 'true' taken 8 times.
✓ Decision 'false' taken 3 times.
11 for (unsigned i = 0; i < q_out_holes.size(); i++) {
465
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 port_t port = circ.get_source_port(q_out_holes[i]);
466
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 Vertex outp = replacement.get_out(Qubit(i));
467
1/2
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
16 vert_lookup.insert(
468 16 {{{vert, port}, ZXPortType::Out},
469
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 {box_bm.right.at(outp), std::nullopt}});
470 }
471
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 3 times.
3 for (unsigned i = 0; i < c_in_holes.size(); i++) {
472 port_t port = circ.get_target_port(c_in_holes[i]);
473 Vertex inp = replacement.get_in(Bit(i));
474 vert_lookup.insert(
475 {{{vert, port}, ZXPortType::In},
476 {box_bm.right.at(inp), std::nullopt}});
477 }
478
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 3 times.
3 for (unsigned i = 0; i < c_out_holes.size(); i++) {
479 port_t port = circ.get_source_port(c_out_holes[i]);
480 Vertex outp = replacement.get_out(Bit(i));
481 vert_lookup.insert(
482 {{{vert, port}, ZXPortType::Out},
483 {box_bm.right.at(outp), std::nullopt}});
484 }
485 3 } else {
486 throw Unsupported(
487 "Cannot convert gate type: " + op->get_name() +
488 " to a ZX node, try rebase the gates to use Rx, Rz, "
489 "X, Z, H, CZ "
490 "or CX. \n");
491 }
492 17 }
493
2/2
✓ Branch 1 taken 204 times.
✓ Branch 2 taken 17 times.
221 }
494
495 // Connect the ZX nodes
496
12/18
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 37 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 183 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 220 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 183 times.
✓ Branch 13 taken 37 times.
✓ Branch 15 taken 183 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 183 times.
✓ Branch 18 taken 37 times.
✓ Branch 20 taken 75 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 37 times.
✓ Branch 23 taken 38 times.
258 BGL_FORALL_EDGES(edge, circ.dag, DAG) {
497
1/2
✓ Branch 1 taken 183 times.
✗ Branch 2 not taken.
183 Vertex v_s = circ.source(edge);
498
1/2
✓ Branch 1 taken 183 times.
✗ Branch 2 not taken.
183 Vertex v_t = circ.target(edge);
499
1/2
✓ Branch 1 taken 183 times.
✗ Branch 2 not taken.
183 port_t p_s = circ.get_source_port(edge);
500
1/2
✓ Branch 1 taken 183 times.
✗ Branch 2 not taken.
183 port_t p_t = circ.get_target_port(edge);
501 // Handle Spiderless ops
502
4/6
✓ Branch 1 taken 183 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 183 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 30 times.
✓ Branch 7 taken 153 times.
2/2
✓ Decision 'true' taken 30 times.
✓ Decision 'false' taken 153 times.
183 if (is_spiderless_optype(circ.get_OpType_from_Vertex(v_s))) {
503 // We only handle in-edges
504 30 continue;
505 }
506
4/6
✓ Branch 1 taken 153 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 153 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 15 times.
✓ Branch 7 taken 138 times.
2/2
✓ Decision 'true' taken 15 times.
✓ Decision 'false' taken 138 times.
153 if (is_spiderless_optype(circ.get_OpType_from_Vertex(v_t))) {
507 // Traverse the path to find the next non-spiderless op
508 15 Edge next_e = edge;
509 do {
510
3/4
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
✓ Branch 4 taken 18 times.
2/2
✓ Decision 'true' taken 12 times.
✓ Decision 'false' taken 18 times.
30 if (circ.get_OpType_from_Vertex(v_t) == OpType::SWAP) {
511 12 next_e = circ.get_nth_out_edge(
512
2/4
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
12 v_t, (circ.get_target_port(next_e) + 1) % 2);
513
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 v_t = circ.target(next_e);
514 } else {
515
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 std::tie(v_t, next_e) = circ.get_next_pair(v_t, next_e);
516 }
517
4/6
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 15 times.
✓ Branch 7 taken 15 times.
0/1
? Decision couldn't be analyzed.
30 } while (is_spiderless_optype(circ.get_OpType_from_Vertex(v_t)));
518
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
15 p_t = circ.get_target_port(next_e);
519 }
520
521 ZXVertPort zx_vp_s =
522
1/2
✓ Branch 3 taken 153 times.
✗ Branch 4 not taken.
153 vert_lookup.at(TypedVertPort(VertPort(v_s, p_s), ZXPortType::Out));
523 ZXVertPort zx_vp_t =
524
1/2
✓ Branch 3 taken 153 times.
✗ Branch 4 not taken.
153 vert_lookup.at(TypedVertPort(VertPort(v_t, p_t), ZXPortType::In));
525
3/4
✓ Branch 1 taken 153 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 124 times.
✓ Branch 4 taken 29 times.
2/2
✓ Decision 'true' taken 124 times.
✓ Decision 'false' taken 29 times.
153 if (circ.get_edgetype(edge) == EdgeType::Quantum) {
526
1/2
✓ Branch 1 taken 124 times.
✗ Branch 2 not taken.
124 zxd.add_wire(
527 zx_vp_s.first, zx_vp_t.first, ZXWireType::Basic, QuantumType::Quantum,
528 zx_vp_s.second, zx_vp_t.second);
529 } else {
530
1/2
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
29 auto bool_it = boolean_outport_lookup.find(VertPort(v_s, p_s));
531
2/2
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 17 times.
2/2
✓ Decision 'true' taken 12 times.
✓ Decision 'false' taken 17 times.
29 if (bool_it == boolean_outport_lookup.end()) {
532
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 zxd.add_wire(
533 zx_vp_s.first, zx_vp_t.first, ZXWireType::Basic,
534 QuantumType::Classical, zx_vp_s.second, zx_vp_t.second);
535 } else {
536 // If the source port is boolean, then connect the copy spider to the
537 // source vertex. All out-edges originated from the source port should
538 // be connected to the copy spider.
539
3/4
✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 9 times.
2/2
✓ Decision 'true' taken 8 times.
✓ Decision 'false' taken 9 times.
17 if (zxd.degree(bool_it->second.first) == 0) {
540 8 zxd.add_wire(
541
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 zx_vp_s.first, bool_it->second.first, ZXWireType::Basic,
542 8 QuantumType::Classical, zx_vp_s.second, bool_it->second.second);
543 }
544 17 zxd.add_wire(
545
1/2
✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
17 bool_it->second.first, zx_vp_t.first, ZXWireType::Basic,
546 17 QuantumType::Classical, bool_it->second.second, zx_vp_t.second);
547 }
548 }
549 }
550 76 return bmap;
551 38 }
552
553 29 std::pair<ZXDiagram, BoundaryVertMap> circuit_to_zx(const Circuit& circ) {
554
1/2
✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
29 ZXDiagram zxd;
555
1/2
✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
29 BoundaryVertMap bmap = circuit_to_zx_recursive(circ, zxd, true);
556 // Remove internal boundary vertices produced by the recursion
557
1/2
✓ Branch 3 taken 29 times.
✗ Branch 4 not taken.
29 ZXVertVec true_boundary = zxd.get_boundary();
558 29 ZXVertIterator vi, vi_end, next;
559
2/4
✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 29 times.
✗ Branch 6 not taken.
29 tie(vi, vi_end) = boost::vertices(*zxd.get_graph());
560
2/2
✓ Branch 1 taken 349 times.
✓ Branch 2 taken 29 times.
2/2
✓ Decision 'true' taken 349 times.
✓ Decision 'false' taken 29 times.
378 for (next = vi; vi != vi_end; vi = next) {
561 349 ++next;
562
1/2
✓ Branch 2 taken 349 times.
✗ Branch 3 not taken.
2/2
✓ Decision 'true' taken 32 times.
✓ Decision 'false' taken 317 times.
349 if ((zxd.get_zxtype(*vi) == ZXType::Input ||
563
7/8
✓ Branch 0 taken 272 times.
✓ Branch 1 taken 77 times.
✓ Branch 4 taken 272 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 77 times.
✓ Branch 7 taken 195 times.
✓ Branch 8 taken 32 times.
✓ Branch 9 taken 122 times.
503 zxd.get_zxtype(*vi) == ZXType::Output) &&
564
1/2
✓ Branch 4 taken 154 times.
✗ Branch 5 not taken.
154 std::find(true_boundary.begin(), true_boundary.end(), *vi) ==
565
2/2
✓ Branch 1 taken 32 times.
✓ Branch 2 taken 317 times.
503 true_boundary.end()) {
566
1/2
✓ Branch 2 taken 32 times.
✗ Branch 3 not taken.
32 WireVec adj_wires = zxd.adj_wires(*vi);
567 TKET_ASSERT(adj_wires.size() == 2);
568 TKET_ASSERT(zxd.get_qtype(adj_wires[0]) == zxd.get_qtype(adj_wires[1]));
569
3/108
✓ Branch 2 taken 32 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 32 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 32 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
✗ Branch 65 not taken.
✗ Branch 66 not taken.
✗ Branch 69 not taken.
✗ Branch 70 not taken.
✗ Branch 72 not taken.
✗ Branch 73 not taken.
✗ Branch 75 not taken.
✗ Branch 76 not taken.
✗ Branch 78 not taken.
✗ Branch 79 not taken.
✗ Branch 81 not taken.
✗ Branch 82 not taken.
✗ Branch 84 not taken.
✗ Branch 85 not taken.
✗ Branch 87 not taken.
✗ Branch 88 not taken.
✗ Branch 90 not taken.
✗ Branch 91 not taken.
✗ Branch 93 not taken.
✗ Branch 94 not taken.
✗ Branch 96 not taken.
✗ Branch 97 not taken.
✗ Branch 100 not taken.
✗ Branch 101 not taken.
✗ Branch 103 not taken.
✗ Branch 104 not taken.
✗ Branch 106 not taken.
✗ Branch 107 not taken.
✗ Branch 109 not taken.
✗ Branch 110 not taken.
✗ Branch 112 not taken.
✗ Branch 113 not taken.
✗ Branch 116 not taken.
✗ Branch 117 not taken.
✗ Branch 120 not taken.
✗ Branch 121 not taken.
✗ Branch 123 not taken.
✗ Branch 124 not taken.
✗ Branch 129 not taken.
✗ Branch 130 not taken.
✗ Branch 132 not taken.
✗ Branch 133 not taken.
✗ Branch 135 not taken.
✗ Branch 136 not taken.
✗ Branch 138 not taken.
✗ Branch 139 not taken.
✗ Branch 141 not taken.
✗ Branch 142 not taken.
✗ Branch 144 not taken.
✗ Branch 145 not taken.
✗ Branch 147 not taken.
✗ Branch 148 not taken.
✗ Branch 150 not taken.
✗ Branch 151 not taken.
✗ Branch 153 not taken.
✗ Branch 154 not taken.
✗ Branch 156 not taken.
✗ Branch 157 not taken.
✗ Branch 159 not taken.
✗ Branch 160 not taken.
✗ Branch 162 not taken.
✗ Branch 163 not taken.
✗ Branch 165 not taken.
✗ Branch 166 not taken.
✗ Branch 169 not taken.
✗ Branch 170 not taken.
✗ Branch 173 not taken.
✗ Branch 174 not taken.
✗ Branch 176 not taken.
✗ Branch 177 not taken.
32 TKET_ASSERT(
570 zxd.get_wire_type(adj_wires[0]) == zxd.get_wire_type(adj_wires[1]));
571
1/2
✓ Branch 2 taken 32 times.
✗ Branch 3 not taken.
32 ZXVertVec neighbours = zxd.neighbours(*vi);
572
3/6
✓ Branch 3 taken 32 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 32 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 32 times.
✗ Branch 10 not taken.
128 zxd.add_wire(
573 64 neighbours[0], neighbours[1], zxd.get_wire_type(adj_wires[0]),
574 32 zxd.get_qtype(adj_wires[0]));
575
1/2
✓ Branch 2 taken 32 times.
✗ Branch 3 not taken.
32 zxd.remove_vertex(*vi);
576 32 }
577 }
578
1/2
✓ Branch 3 taken 29 times.
✗ Branch 4 not taken.
58 return {std::move(zxd), std::move(bmap)};
579 29 }
580
581 50 void clean_frontier(
582 ZXDiagram& diag, ZXVertVec& frontier, Circuit& circ,
583 std::map<ZXVert, unsigned>& qubit_map) {
584 50 std::set<ZXVert> frontier_lookup;
585 50 std::list<std::pair<ZXVert, ZXVert>> czs;
586
2/2
✓ Branch 5 taken 205 times.
✓ Branch 6 taken 50 times.
2/2
✓ Decision 'true' taken 205 times.
✓ Decision 'false' taken 50 times.
255 for (const ZXVert& f : frontier) {
587
1/2
✓ Branch 1 taken 205 times.
✗ Branch 2 not taken.
205 frontier_lookup.insert(f);
588
3/4
✓ Branch 1 taken 205 times.
✗ Branch 2 not taken.
✓ Branch 8 taken 631 times.
✓ Branch 9 taken 205 times.
0/1
? Decision couldn't be analyzed.
836 for (const Wire& w : diag.adj_wires(f)) {
589
1/2
✓ Branch 1 taken 631 times.
✗ Branch 2 not taken.
631 ZXVert n = diag.other_end(w, f);
590
3/4
✓ Branch 1 taken 631 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 205 times.
✓ Branch 4 taken 426 times.
2/2
✓ Decision 'true' taken 205 times.
✓ Decision 'false' taken 426 times.
631 if (diag.get_zxtype(n) == ZXType::Output) {
591
1/2
✓ Branch 1 taken 205 times.
✗ Branch 2 not taken.
205 unsigned q = qubit_map.at(n);
592
3/4
✓ Branch 1 taken 205 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 48 times.
✓ Branch 4 taken 157 times.
2/2
✓ Decision 'true' taken 48 times.
✓ Decision 'false' taken 157 times.
205 if (diag.get_wire_type(w) == ZXWireType::H) {
593
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 diag.set_wire_type(w, ZXWireType::Basic);
594
2/4
✓ Branch 3 taken 48 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 48 times.
✗ Branch 7 not taken.
48 circ.add_op<unsigned>(OpType::H, {q});
595 }
596
3/7
✓ Branch 1 taken 205 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 38 times.
✓ Branch 5 taken 167 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
205 switch (diag.get_zxtype(f)) {
597
0/1
✗ Decision 'true' not taken.
case ZXType::Input: {
598 break;
599 }
600
1/1
✓ Decision 'true' taken 38 times.
38 case ZXType::XY: {
601
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 const PhasedGen& f_gen = diag.get_vertex_ZXGen<PhasedGen>(f);
602
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 Expr ph = f_gen.get_param();
603
3/4
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 21 times.
✓ Branch 4 taken 17 times.
2/2
✓ Decision 'true' taken 21 times.
✓ Decision 'false' taken 17 times.
38 if (!equiv_0(ph)) {
604
3/6
✓ Branch 3 taken 21 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 21 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 21 times.
✗ Branch 10 not taken.
21 circ.add_op<unsigned>(OpType::U1, -ph, {q});
605 }
606
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 diag.set_vertex_ZXGen_ptr(
607
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
76 f, ZXGen::create_gen(ZXType::PX, QuantumType::Quantum));
608 38 break;
609 38 }
610
1/1
✓ Decision 'true' taken 167 times.
167 case ZXType::PX: {
611
1/2
✓ Branch 1 taken 167 times.
✗ Branch 2 not taken.
167 const CliffordGen& f_gen = diag.get_vertex_ZXGen<CliffordGen>(f);
612
2/4
✓ Branch 1 taken 167 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 167 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 167 times.
167 if (f_gen.get_param()) {
613 circ.add_op<unsigned>(OpType::Z, {q});
614 diag.set_vertex_ZXGen_ptr(
615 f, ZXGen::create_gen(ZXType::PX, QuantumType::Quantum));
616 }
617 167 break;
618 }
619
0/1
✗ Decision 'true' not taken.
case ZXType::PY: {
620 const CliffordGen& f_gen = diag.get_vertex_ZXGen<CliffordGen>(f);
621 circ.add_op<unsigned>(
622 f_gen.get_param() ? OpType::S : OpType::Sdg, {q});
623 diag.set_vertex_ZXGen_ptr(
624 f, ZXGen::create_gen(ZXType::PX, QuantumType::Quantum));
625 break;
626 }
627
0/1
✗ Decision 'true' not taken.
default:
628 throw ZXError(
629 "Error during extraction from ZX diagram: unexpected ZXType in "
630 "frontier");
631 }
632
3/4
✓ Branch 2 taken 426 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 28 times.
✓ Branch 6 taken 398 times.
2/2
✓ Decision 'true' taken 28 times.
✓ Decision 'false' taken 398 times.
426 } else if (frontier_lookup.find(n) != frontier_lookup.end()) {
633
1/2
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
28 czs.push_back({f, n});
634
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 diag.remove_wire(w);
635 }
636 205 }
637 }
638
2/2
✓ Branch 4 taken 28 times.
✓ Branch 5 taken 50 times.
2/2
✓ Decision 'true' taken 28 times.
✓ Decision 'false' taken 50 times.
78 for (const std::pair<ZXVert, ZXVert>& pair : czs) {
639
2/4
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 28 times.
✗ Branch 6 not taken.
56 circ.add_op<unsigned>(
640
2/4
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 28 times.
✗ Branch 5 not taken.
28 OpType::CZ, {qubit_map.at(pair.first), qubit_map.at(pair.second)});
641 }
642 100 ZXVertVec new_frontier;
643
2/2
✓ Branch 5 taken 205 times.
✓ Branch 6 taken 50 times.
2/2
✓ Decision 'true' taken 205 times.
✓ Decision 'false' taken 50 times.
255 for (const ZXVert& f : frontier) {
644 205 bool removed = false;
645
1/2
✓ Branch 1 taken 205 times.
✗ Branch 2 not taken.
205 ZXVertVec ns = diag.neighbours(f);
646
2/2
✓ Branch 1 taken 96 times.
✓ Branch 2 taken 109 times.
2/2
✓ Decision 'true' taken 96 times.
✓ Decision 'false' taken 109 times.
205 if (ns.size() == 2) {
647
2/4
✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 96 times.
✗ Branch 5 not taken.
96 ZXType nt0 = diag.get_zxtype(ns.at(0));
648
2/4
✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 96 times.
✗ Branch 5 not taken.
96 ZXType nt1 = diag.get_zxtype(ns.at(1));
649
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 69 times.
✓ Branch 5 taken 27 times.
2/2
✓ Decision 'true' taken 8 times.
✓ Decision 'false' taken 88 times.
96 if ((nt0 == ZXType::Input && nt1 == ZXType::Output) ||
650
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 61 times.
69 (nt0 == ZXType::Output && nt1 == ZXType::Input)) {
651
3/6
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 8 times.
✗ Branch 10 not taken.
8 diag.add_wire(ns.at(0), ns.at(1));
652
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 diag.remove_vertex(f);
653 8 removed = true;
654 }
655 }
656
6/8
✓ Branch 0 taken 197 times.
✓ Branch 1 taken 8 times.
✓ Branch 3 taken 197 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 197 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 197 times.
✓ Branch 8 taken 8 times.
2/2
✓ Decision 'true' taken 197 times.
✓ Decision 'false' taken 8 times.
205 if (!removed && diag.get_zxtype(f) != ZXType::Input)
657
1/2
✓ Branch 1 taken 197 times.
✗ Branch 2 not taken.
197 new_frontier.push_back(f);
658 205 }
659
1/2
✓ Branch 1 taken 50 times.
✗ Branch 2 not taken.
50 frontier = new_frontier;
660 50 }
661
662 38 ZXVertSeqSet neighbours_of_frontier(
663 const ZXDiagram& diag, const ZXVertVec& frontier) {
664 38 ZXVertSeqSet n_set;
665
2/2
✓ Branch 5 taken 147 times.
✓ Branch 6 taken 38 times.
2/2
✓ Decision 'true' taken 147 times.
✓ Decision 'false' taken 38 times.
185 for (const ZXVert& f : frontier) {
666
3/4
✓ Branch 1 taken 147 times.
✗ Branch 2 not taken.
✓ Branch 8 taken 395 times.
✓ Branch 9 taken 147 times.
0/1
? Decision couldn't be analyzed.
542 for (const Wire& w : diag.adj_wires(f)) {
667
1/2
✓ Branch 1 taken 395 times.
✗ Branch 2 not taken.
395 ZXVert n = diag.other_end(w, f);
668
1/2
✓ Branch 1 taken 395 times.
✗ Branch 2 not taken.
395 ZXType n_type = diag.get_zxtype(n);
669
3/4
✓ Branch 0 taken 248 times.
✓ Branch 1 taken 147 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 248 times.
2/2
✓ Decision 'true' taken 248 times.
✓ Decision 'false' taken 147 times.
395 if (n_type == ZXType::Output || n_type == ZXType::Input) continue;
670
1/2
✓ Branch 1 taken 248 times.
✗ Branch 2 not taken.
248 n_set.insert(n);
671 147 }
672 }
673 38 return n_set;
674 }
675
676 50 static void bipartite_complementation(
677 ZXDiagram& diag, const ZXVertSeqSet& sa, const ZXVertSeqSet& sb) {
678
5/8
✓ Branch 4 taken 40 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 40 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 90 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 40 times.
✓ Branch 13 taken 50 times.
0/1
? Decision couldn't be analyzed.
90 for (const ZXVert& a : sa.get<TagSeq>()) {
679
5/8
✓ Branch 4 taken 161 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 161 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 201 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 161 times.
✓ Branch 13 taken 40 times.
0/1
? Decision couldn't be analyzed.
201 for (const ZXVert& b : sb.get<TagSeq>()) {
680
1/2
✓ Branch 1 taken 161 times.
✗ Branch 2 not taken.
161 std::optional<Wire> wire = diag.wire_between(a, b);
681
2/2
✓ Branch 1 taken 78 times.
✓ Branch 2 taken 83 times.
2/2
✓ Decision 'true' taken 78 times.
✓ Decision 'false' taken 83 times.
161 if (wire)
682
1/2
✓ Branch 2 taken 78 times.
✗ Branch 3 not taken.
78 diag.remove_wire(*wire);
683 else
684
1/2
✓ Branch 3 taken 83 times.
✗ Branch 4 not taken.
83 diag.add_wire(a, b, ZXWireType::H);
685 }
686 }
687 50 }
688
689 100 void extend_if_input(
690 ZXDiagram& diag, const ZXVert& v, std::map<ZXVert, ZXVert>& input_qubits) {
691
1/2
✓ Branch 1 taken 100 times.
✗ Branch 2 not taken.
100 std::map<ZXVert, ZXVert>::iterator found = input_qubits.find(v);
692
2/2
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 95 times.
2/2
✓ Decision 'true' taken 5 times.
✓ Decision 'false' taken 95 times.
100 if (found != input_qubits.end()) {
693 5 ZXVert in = found->second;
694
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 ZXVert ext0 = diag.add_vertex(ZXType::XY);
695
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 ZXVert ext1 = diag.add_vertex(ZXType::XY);
696
3/6
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 5 times.
✗ Branch 8 not taken.
5 diag.remove_wire(diag.adj_wires(in).at(0));
697
1/2
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
5 diag.add_wire(in, ext0);
698
1/2
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
5 diag.add_wire(ext0, ext1, ZXWireType::H);
699
1/2
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
5 diag.add_wire(ext1, v, ZXWireType::H);
700
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 input_qubits.erase(found);
701
1/2
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 input_qubits.insert({ext0, in});
702 }
703 100 }
704
705 38 bool remove_all_gadgets(
706 ZXDiagram& diag, const ZXVertVec& frontier,
707 std::map<ZXVert, ZXVert>& input_qubits) {
708 38 bool removed_gadget = false;
709
2/2
✓ Branch 5 taken 147 times.
✓ Branch 6 taken 38 times.
2/2
✓ Decision 'true' taken 147 times.
✓ Decision 'false' taken 38 times.
185 for (const ZXVert& f : frontier) {
710
1/2
✓ Branch 1 taken 147 times.
✗ Branch 2 not taken.
147 ZXVertVec f_ns = diag.neighbours(f);
711 147 std::optional<ZXVert> found_output;
712
1/2
✓ Branch 5 taken 282 times.
✗ Branch 6 not taken.
1/2
✓ Decision 'true' taken 282 times.
✗ Decision 'false' not taken.
282 for (const ZXVert& n : f_ns) {
713 // Each frontier vertex is connected to a unique output, find it
714
3/4
✓ Branch 1 taken 282 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 147 times.
✓ Branch 4 taken 135 times.
2/2
✓ Decision 'true' taken 147 times.
✓ Decision 'false' taken 135 times.
282 if (diag.get_zxtype(n) == ZXType::Output) {
715 147 found_output = n;
716 147 break;
717 }
718 }
719
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 147 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 147 times.
147 if (!found_output)
720 throw ZXError(
721 "Error during extraction from ZXDiagram: frontier vertex not "
722 "adjacent to an output");
723 147 ZXVert o = *found_output;
724
2/2
✓ Branch 5 taken 385 times.
✓ Branch 6 taken 137 times.
2/2
✓ Decision 'true' taken 385 times.
✓ Decision 'false' taken 137 times.
522 for (const ZXVert& n : f_ns) {
725
3/4
✓ Branch 1 taken 385 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 375 times.
2/2
✓ Decision 'true' taken 10 times.
✓ Decision 'false' taken 375 times.
385 if (diag.get_zxtype(n) == ZXType::YZ) {
726 // Pivot
727 // Identify three subsets of neighbours
728
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 ZXVertSeqSet excl_f;
729 // Need to recalculate neighbours rather than use f_ns as we might have
730 // extended
731
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 extend_if_input(diag, f, input_qubits);
732
3/4
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 8 taken 65 times.
✓ Branch 9 taken 10 times.
0/1
? Decision couldn't be analyzed.
75 for (const ZXVert& n : diag.neighbours(f)) {
733
1/2
✓ Branch 1 taken 65 times.
✗ Branch 2 not taken.
65 extend_if_input(diag, n, input_qubits);
734
1/2
✓ Branch 1 taken 65 times.
✗ Branch 2 not taken.
65 excl_f.insert(n);
735 10 }
736
1/2
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 excl_f.get<TagKey>().erase(n);
737
1/2
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 excl_f.get<TagKey>().erase(o);
738
2/4
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
10 ZXVertSeqSet excl_n, joint;
739 10 auto& lookup_f = excl_f.get<TagKey>();
740
3/4
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 8 taken 25 times.
✓ Branch 9 taken 10 times.
0/1
? Decision couldn't be analyzed.
35 for (const ZXVert& nn : diag.neighbours(n)) {
741
1/2
✓ Branch 1 taken 25 times.
✗ Branch 2 not taken.
25 extend_if_input(diag, nn, input_qubits);
742
4/6
✓ Branch 2 taken 25 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 25 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 5 times.
✓ Branch 8 taken 20 times.
2/2
✓ Decision 'true' taken 5 times.
✓ Decision 'false' taken 20 times.
25 if (lookup_f.find(nn) != lookup_f.end())
743
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 joint.insert(nn);
744 else
745
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 excl_n.insert(nn);
746 10 }
747
1/2
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 excl_n.get<TagKey>().erase(f);
748
5/8
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 5 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 15 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 5 times.
✓ Branch 13 taken 10 times.
0/1
? Decision couldn't be analyzed.
15 for (const ZXVert& nn : joint.get<TagSeq>())
749
1/2
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 excl_f.get<TagKey>().erase(nn);
750 // The is_MBQC check in zx_to_circuit guarantees QuantumType::Quantum
751
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 bipartite_complementation(diag, joint, excl_n);
752
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 bipartite_complementation(diag, joint, excl_f);
753
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 bipartite_complementation(diag, excl_n, excl_f);
754 // In place of switching vertices f and n, we invert their
755 // connectivities
756
1/2
✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
10 excl_n.insert(excl_f.begin(), excl_f.end());
757
3/6
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 10 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 10 times.
✗ Branch 9 not taken.
10 bipartite_complementation(diag, {f}, excl_n);
758
3/6
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 10 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 10 times.
✗ Branch 9 not taken.
10 bipartite_complementation(diag, {n}, excl_n);
759
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 Wire ow = *diag.wire_between(f, o);
760
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 diag.set_wire_type(
761
2/4
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
10 ow, (diag.get_wire_type(ow) == ZXWireType::Basic)
762 ? ZXWireType::H
763 : ZXWireType::Basic);
764
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 const PhasedGen& yz_gen = diag.get_vertex_ZXGen<PhasedGen>(n);
765
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 diag.set_vertex_ZXGen_ptr(
766
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
20 n, ZXGen::create_gen(
767
2/4
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
20 ZXType::XY, -yz_gen.get_param(), QuantumType::Quantum));
768
5/8
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 5 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 15 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 5 times.
✓ Branch 13 taken 10 times.
0/1
? Decision couldn't be analyzed.
15 for (const ZXVert& nn : joint.get<TagSeq>()) {
769 5 ZXGen_ptr new_gen;
770
2/7
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
5 switch (diag.get_zxtype(nn)) {
771
1/1
✓ Decision 'true' taken 5 times.
5 case ZXType::XY: {
772
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 const PhasedGen& ph_gen = diag.get_vertex_ZXGen<PhasedGen>(nn);
773
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
10 new_gen = ZXGen::create_gen(
774
3/6
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 5 times.
✗ Branch 8 not taken.
15 ZXType::XY, ph_gen.get_param() + 1., QuantumType::Quantum);
775 5 break;
776 }
777 case ZXType::PX:
778 case ZXType::PY: {
779 const CliffordGen& cl_gen =
780 diag.get_vertex_ZXGen<CliffordGen>(nn);
781 new_gen = ZXGen::create_gen(
782 cl_gen.get_type(), !cl_gen.get_param(), QuantumType::Quantum);
783 break;
784 }
785 case ZXType::XZ:
786 case ZXType::YZ: {
787 const PhasedGen& ph_gen = diag.get_vertex_ZXGen<PhasedGen>(nn);
788 new_gen = ZXGen::create_gen(
789 ph_gen.get_type(), -ph_gen.get_param(), QuantumType::Quantum);
790 break;
791 }
792
0/1
✗ Decision 'true' not taken.
case ZXType::PZ: {
793 new_gen = diag.get_vertex_ZXGen_ptr(nn);
794 break;
795 }
796
0/1
✗ Decision 'true' not taken.
default:
797 throw ZXError(
798 "Error during extraction from ZX diagram: unexpected ZXType "
799 "during local complementation");
800 }
801
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 diag.set_vertex_ZXGen_ptr(nn, new_gen);
802 5 }
803 10 removed_gadget = true;
804 10 break;
805 10 }
806 }
807 147 }
808 38 return removed_gadget;
809 }
810
811 2 Circuit zx_to_circuit(const ZXDiagram& d) {
812
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 ZXDiagram diag = d;
813
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 2 times.
2 if (!diag.is_MBQC())
814 throw ZXError("Can only extract a circuit from a ZX diagram in MBQC form");
815
1/2
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 ZXVertVec ins = diag.get_boundary(ZXType::Input);
816
1/2
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 ZXVertVec outs = diag.get_boundary(ZXType::Output);
817
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 2 times.
2 if (ins.size() != outs.size())
818 throw ZXError("Can only extract a circuit from a unitary ZX diagram");
819
820
1/2
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 Circuit circ(ins.size());
821
822 2 ZXVertVec frontier;
823 2 std::map<ZXVert, unsigned> qubit_map;
824 2 unsigned q = 0;
825
2/2
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 2 times.
2/2
✓ Decision 'true' taken 8 times.
✓ Decision 'false' taken 2 times.
10 for (const ZXVert& o : outs) {
826
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 ZXVert f_i = diag.neighbours(o).at(0);
827
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 frontier.push_back(f_i);
828
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 qubit_map.insert({o, q});
829
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 qubit_map.insert({f_i, q});
830 8 ++q;
831 }
832 2 std::map<ZXVert, ZXVert> input_qubits;
833
2/2
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 2 times.
2/2
✓ Decision 'true' taken 8 times.
✓ Decision 'false' taken 2 times.
10 for (const ZXVert& i : ins)
834
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
8 input_qubits.insert({diag.neighbours(i).at(0), i});
835
836
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 clean_frontier(diag, frontier, circ, qubit_map);
837
2/2
✓ Branch 1 taken 38 times.
✓ Branch 2 taken 2 times.
2/2
✓ Decision 'true' taken 38 times.
✓ Decision 'false' taken 2 times.
40 while (!frontier.empty()) {
838
3/4
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 28 times.
2/2
✓ Decision 'true' taken 10 times.
✓ Decision 'false' taken 28 times.
38 if (remove_all_gadgets(diag, frontier, input_qubits)) {
839
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 clean_frontier(diag, frontier, circ, qubit_map);
840 }
841
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 ZXVertSeqSet neighbours = neighbours_of_frontier(diag, frontier);
842
3/6
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 38 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 38 times.
✗ Branch 11 not taken.
114 boost::bimap<ZXVert, unsigned> correctors, preserve, ys;
843 38 ZXVertVec to_solve;
844
2/2
✓ Branch 5 taken 147 times.
✓ Branch 6 taken 38 times.
2/2
✓ Decision 'true' taken 147 times.
✓ Decision 'false' taken 38 times.
185 for (const ZXVert& f : frontier) {
845
2/4
✓ Branch 2 taken 147 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 147 times.
✗ Branch 6 not taken.
1/2
✓ Decision 'true' taken 147 times.
✗ Decision 'false' not taken.
147 if (input_qubits.find(f) == input_qubits.end())
846
3/6
✓ Branch 1 taken 147 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 147 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 147 times.
✗ Branch 8 not taken.
147 correctors.insert({f, (unsigned)correctors.size()});
847 }
848
5/8
✓ Branch 4 taken 176 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 176 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 214 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 176 times.
✓ Branch 13 taken 38 times.
0/1
? Decision couldn't be analyzed.
214 for (const ZXVert& n : neighbours.get<TagSeq>()) {
849
1/2
✓ Branch 1 taken 176 times.
✗ Branch 2 not taken.
176 ZXType n_type = diag.get_zxtype(n);
850
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 176 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
176 if (n_type == ZXType::XY || n_type == ZXType::PX ||
851 n_type == ZXType::PY) {
852
3/6
✓ Branch 1 taken 176 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 176 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 176 times.
✗ Branch 8 not taken.
176 preserve.insert({n, (unsigned)preserve.size()});
853
1/2
✓ Branch 1 taken 176 times.
✗ Branch 2 not taken.
176 to_solve.push_back(n);
854 }
855 }
856 std::map<ZXVert, ZXVertSeqSet> candidates =
857
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 Flow::gauss_solve_correctors(diag, correctors, preserve, to_solve, ys);
858
859
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
38 if (candidates.empty())
860 throw ZXError(
861 "Error during extraction from ZX diagram: diagram does not have "
862 "gflow");
863
864 38 unsigned min = UINT_MAX;
865 ZXVert best;
866
2/2
✓ Branch 5 taken 109 times.
✓ Branch 6 taken 38 times.
147 for (const std::pair<const ZXVert, ZXVertSeqSet>& p : candidates) {
867
2/2
✓ Branch 1 taken 51 times.
✓ Branch 2 taken 58 times.
109 if (p.second.size() < min) {
868 51 min = p.second.size();
869 51 best = p.first;
870 }
871 }
872
2/4
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
38 ZXVertSeqSet g_best = candidates.at(best);
873
874
1/2
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 ZXVert f_to_isolate = g_best.get<TagSeq>().front();
875
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 unsigned f_q = qubit_map.at(f_to_isolate);
876
5/8
✓ Branch 3 taken 41 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 41 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 79 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 41 times.
✓ Branch 12 taken 38 times.
79 for (const ZXVert& f : g_best) {
877
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 38 times.
41 if (f != f_to_isolate) {
878
3/6
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 3 times.
✗ Branch 10 not taken.
3 circ.add_op<unsigned>(OpType::CX, {f_q, qubit_map.at(f)});
879 }
880 }
881 ZXVert out;
882
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 Wire w_out;
883
2/4
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 8 taken 53 times.
✗ Branch 9 not taken.
53 for (const Wire& w : diag.adj_wires(f_to_isolate)) {
884
1/2
✓ Branch 1 taken 53 times.
✗ Branch 2 not taken.
53 ZXVert n = diag.other_end(w, f_to_isolate);
885
3/4
✓ Branch 1 taken 53 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 38 times.
✓ Branch 4 taken 15 times.
53 if (diag.get_zxtype(n) == ZXType::Output) {
886 38 out = n;
887 38 w_out = w;
888 38 break;
889 }
890 38 }
891
1/2
✓ Branch 3 taken 38 times.
✗ Branch 4 not taken.
76 diag.add_wire(
892 best, out,
893
2/4
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 38 times.
✗ Branch 4 not taken.
38 (diag.get_wire_type(w_out) == ZXWireType::Basic) ? ZXWireType::H
894 : ZXWireType::Basic);
895
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 diag.remove_vertex(f_to_isolate);
896
2/4
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
38 qubit_map.erase(qubit_map.find(f_to_isolate));
897
1/2
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 qubit_map.insert({best, f_q});
898
1/2
✓ Branch 3 taken 87 times.
✗ Branch 4 not taken.
87 for (ZXVertVec::iterator it = frontier.begin(); it != frontier.end();
899 49 ++it) {
900
2/2
✓ Branch 1 taken 38 times.
✓ Branch 2 taken 49 times.
87 if (*it == f_to_isolate) {
901 38 *it = best;
902 38 break;
903 }
904 }
905
906
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 clean_frontier(diag, frontier, circ, qubit_map);
907 38 }
908
909 2 qubit_map_t qm;
910
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 2 times.
10 for (unsigned i = 0; i < ins.size(); ++i) {
911
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 ZXVert in = ins.at(i);
912
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 ZXVert out = diag.neighbours(in).at(0);
913
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
8 if (diag.get_zxtype(out) != ZXType::Output)
914 throw ZXError(
915 "Error during extraction from ZX diagram: input not adjacent to "
916 "output after extracting");
917
4/8
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 8 times.
✗ Branch 12 not taken.
8 qm.insert({Qubit(qubit_map.at(out)), Qubit(i)});
918 }
919
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 circ.permute_boundary_output(qm);
920
921 // Reverse gates in circuit (all gates added are self-transpose)
922
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 return circ.transpose();
923 2 }
924
925 } // namespace tket
926