GCC Code Coverage Report


Directory: ./
File: ZX/ZXRWDecompositions.cpp
Date: 2022-10-15 05:10:18
Warnings: 4 unchecked decisions!
Exec Total Coverage
Lines: 256 287 89.2%
Functions: 8 8 100.0%
Branches: 346 660 52.4%
Decisions: 48 74 64.9%

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 "Utils/Constants.hpp"
16 #include "Utils/GraphHeaders.hpp"
17 #include "ZX/Rewrite.hpp"
18
19 namespace tket {
20
21 namespace zx {
22
23 5 bool Rewrite::decompose_boxes_fun(ZXDiagram& diag) {
24 5 bool success = false;
25 5 std::list<ZXVert> to_decompose;
26
7/8
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 17 times.
✓ Branch 7 taken 5 times.
✓ Branch 9 taken 17 times.
✓ Branch 10 taken 5 times.
✓ Branch 12 taken 5 times.
✓ Branch 13 taken 5 times.
27 BGL_FORALL_VERTICES(v, *diag.graph, ZXGraph) {
27
4/6
✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 14 times.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
2/2
✓ Decision 'true' taken 8 times.
✓ Decision 'false' taken 9 times.
17 if (diag.get_zxtype(v) == ZXType::ZXBox) to_decompose.push_back(v);
28 }
29
2/2
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 5 times.
2/2
✓ Decision 'true' taken 3 times.
✓ Decision 'false' taken 5 times.
8 for (const ZXVert& box : to_decompose) {
30
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 const ZXBox& zxb = diag.get_vertex_ZXGen<ZXBox>(box);
31
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
3 ZXDiagram inner(*zxb.get_diagram());
32
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 decompose_boxes_fun(inner);
33
2/4
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
3 std::vector<std::pair<Wire, WireEnd>> sub_boundary(zxb.n_ports());
34
3/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 8 taken 7 times.
✓ Branch 9 taken 3 times.
0/1
? Decision couldn't be analyzed.
10 for (const Wire& w : diag.adj_wires(box)) {
35
3/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 1 times.
2/2
✓ Decision 'true' taken 6 times.
✓ Decision 'false' taken 1 times.
7 if (diag.source(w) == box)
36
2/4
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 6 times.
✗ Branch 7 not taken.
6 sub_boundary.at(*diag.source_port(w)) = {w, WireEnd::Source};
37
3/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 6 times.
2/2
✓ Decision 'true' taken 1 times.
✓ Decision 'false' taken 6 times.
7 if (diag.target(w) == box)
38
2/4
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
1 sub_boundary.at(*diag.target_port(w)) = {w, WireEnd::Target};
39 3 }
40
3/6
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
6 ZXDiagram::Subdiagram sub{sub_boundary, {box}};
41
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 diag.substitute(inner, sub);
42 3 success = true;
43 3 }
44 5 return success;
45 5 }
46
47
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 Rewrite Rewrite::decompose_boxes() { return Rewrite(decompose_boxes_fun); }
48
49 2 bool Rewrite::basic_wires_fun(ZXDiagram& diag) {
50 ZXGen_ptr qhad =
51
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 std::make_shared<const PhasedGen>(ZXType::Hbox, -1, QuantumType::Quantum);
52 4 ZXGen_ptr chad = std::make_shared<const PhasedGen>(
53
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 ZXType::Hbox, -1, QuantumType::Classical);
54 2 WireVec targets;
55
12/18
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 10 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 12 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 10 times.
✓ Branch 14 taken 2 times.
✓ Branch 16 taken 10 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 10 times.
✓ Branch 19 taken 2 times.
✓ Branch 21 taken 4 times.
✗ Branch 22 not taken.
✓ Branch 23 taken 2 times.
✓ Branch 24 taken 2 times.
14 BGL_FORALL_EDGES(w, *diag.graph, ZXGraph) {
56
4/6
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 8 times.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
2/2
✓ Decision 'true' taken 4 times.
✓ Decision 'false' taken 6 times.
10 if (diag.get_wire_type(w) == ZXWireType::H) targets.push_back(w);
57 }
58
2/2
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 2 times.
2/2
✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 2 times.
4 for (const Wire& w : targets) {
59
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 WireProperties wp = diag.get_wire_info(w);
60
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 ZXGen_ptr had = (wp.qtype == QuantumType::Quantum) ? qhad : chad;
61
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 ZXVert h = diag.add_vertex(had);
62
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 ZXVert s = diag.source(w);
63
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 ZXVert t = diag.target(w);
64 2 wp.type = ZXWireType::Basic;
65 2 WireProperties wp2 = wp;
66 2 wp.target_port = std::nullopt;
67 2 wp2.source_port = std::nullopt;
68
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 diag.add_wire(s, h, wp);
69
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 diag.add_wire(h, t, wp);
70
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 diag.remove_wire(w);
71 2 }
72 4 return !targets.empty();
73 2 }
74
75
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 Rewrite Rewrite::basic_wires() { return Rewrite(basic_wires_fun); }
76
77 5 bool Rewrite::rebase_to_zx_fun(ZXDiagram& diag) {
78 5 ZXVertVec verts;
79
7/8
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 60 times.
✓ Branch 7 taken 5 times.
✓ Branch 9 taken 60 times.
✓ Branch 10 taken 5 times.
✓ Branch 12 taken 5 times.
✓ Branch 13 taken 5 times.
70 BGL_FORALL_VERTICES(v, *diag.graph, ZXGraph) {
80
1/2
✓ Branch 1 taken 60 times.
✗ Branch 2 not taken.
60 ZXType t = diag.get_zxtype(v);
81
9/12
✓ Branch 1 taken 60 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 42 times.
✓ Branch 4 taken 18 times.
✓ Branch 6 taken 42 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 26 times.
✓ Branch 9 taken 16 times.
✓ Branch 10 taken 26 times.
✓ Branch 11 taken 34 times.
✓ Branch 13 taken 26 times.
✗ Branch 14 not taken.
2/2
✓ Decision 'true' taken 31 times.
✓ Decision 'false' taken 29 times.
60 if (!is_boundary_type(t) && !is_spider_type(t)) verts.push_back(v);
82 }
83
2/2
✓ Branch 5 taken 26 times.
✓ Branch 6 taken 5 times.
2/2
✓ Decision 'true' taken 26 times.
✓ Decision 'false' taken 5 times.
31 for (const ZXVert& v : verts) {
84
9/11
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 10 taken 16 times.
✗ Branch 11 not taken.
26 switch (diag.get_zxtype(v)) {
85
0/1
✗ Decision 'true' not taken.
4 case ZXType::Hbox: {
86 // Use a combination of the equations in doi:10.1145/3209108.3209128 and
87 // doi:10.4204/EPTCS.340.16 Iteratively decompose based on the phase, so
88 // cannot be applied to symbolic phases
89
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 const PhasedGen& vg = diag.get_vertex_ZXGen<PhasedGen>(v);
90
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 std::optional<Complex> opt_ph = eval_expr_c(vg.get_param());
91
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 4 times.
4 if (!opt_ph)
92 throw ZXError(
93 "Hbox with symbolic phase cannot be decomposed into ZX "
94 "generators");
95
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 QuantumType qt = *vg.get_qtype();
96
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 unsigned deg = diag.degree(v);
97
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 WireVec v_ws = diag.adj_wires(v);
98 4 std::vector<std::pair<Wire, WireEnd>> v_bounds;
99
100
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 ZXDiagram rep(0, 0, 0, 0);
101 4 double r = std::abs(*opt_ph - 1.);
102 4 double ph = std::arg(*opt_ph - 1.) / PI;
103 // Reduce r to the range [0, 2]
104
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 4 times.
4 if (r < 0.) {
105 r *= -1.;
106 ph += 1.;
107 }
108 // Core is an algebraic Zbox surrounded by triangles
109
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 ZXVert zph = rep.add_vertex(ZXType::ZSpider, Expr(ph), qt);
110 // Not every will may have the same QuantumType in general
111 4 bool all_same_qt = true;
112
3/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 8 taken 11 times.
✓ Branch 9 taken 4 times.
0/1
? Decision couldn't be analyzed.
15 for (const Wire& w : diag.adj_wires(v)) {
113
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 QuantumType wqt = diag.get_qtype(w);
114
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 8 times.
1/2
✓ Decision 'true' taken 11 times.
✗ Decision 'false' not taken.
11 if (qt != wqt) all_same_qt = false;
115
3/4
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 5 times.
2/2
✓ Decision 'true' taken 6 times.
✓ Decision 'false' taken 5 times.
11 if (diag.source(w) == v) {
116
1/2
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 v_bounds.push_back({w, WireEnd::Source});
117
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 ZXVert bound = rep.add_vertex(ZXType::Open, wqt);
118
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 rep.boundary.push_back(bound);
119
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 ZXVert tri = rep.add_vertex(ZXType::Triangle, wqt);
120
1/2
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
6 rep.add_wire(bound, tri, ZXWireType::Basic, wqt, std::nullopt, 0);
121
1/2
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
6 rep.add_wire(tri, zph, ZXWireType::Basic, wqt, 1, std::nullopt);
122 }
123
3/4
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 5 times.
2/2
✓ Decision 'true' taken 6 times.
✓ Decision 'false' taken 5 times.
11 if (diag.target(w) == v) {
124
1/2
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 v_bounds.push_back({w, WireEnd::Target});
125
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 ZXVert bound = rep.add_vertex(ZXType::Open, wqt);
126
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 rep.boundary.push_back(bound);
127
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 ZXVert tri = rep.add_vertex(ZXType::Triangle, wqt);
128
1/2
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
6 rep.add_wire(bound, tri, ZXWireType::Basic, wqt, std::nullopt, 0);
129
1/2
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
6 rep.add_wire(tri, zph, ZXWireType::Basic, wqt, 1, std::nullopt);
130 }
131 4 }
132 // Special cases for degree 2
133
5/8
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
2/2
✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 4 times.
6 if (deg == 2 && all_same_qt && approx_eq(opt_ph->real(), -1.) &&
134
7/12
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✓ Branch 11 taken 2 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 2 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
6 approx_0(opt_ph->imag())) {
135
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 WireVec ws = diag.adj_wires(v);
136
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 2 times.
2 if (ws.size() == 1) {
137 // self-loop
138 diag.multiply_scalar(0.);
139 } else {
140 // Replace with a Hadamard edge
141
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 ZXVert s = diag.other_end(ws.at(0), v);
142
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 ZXVert t = diag.other_end(ws.at(1), v);
143 2 unsigned n_hs = 0;
144
3/6
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
1/2
✓ Decision 'true' taken 2 times.
✗ Decision 'false' not taken.
2 if (diag.get_wire_type(ws.at(0)) == ZXWireType::H) ++n_hs;
145
3/6
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
0/1
? Decision couldn't be analyzed.
2 if (diag.get_wire_type(ws.at(1)) == ZXWireType::H) ++n_hs;
146 4 diag.add_wire(
147
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 s, t, (n_hs == 1) ? ZXWireType::Basic : ZXWireType::H, qt);
148 }
149
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 diag.remove_vertex(v);
150 2 break;
151 2 }
152 // Using the algebraic fusion rule, can break off 2-boxes (0-phase
153 // ZSpider and a triangle)
154
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 for (; r > 2.; r -= 1) {
155
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 ZXVert tri = rep.add_vertex(ZXType::Triangle, qt);
156
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 ZXVert one = rep.add_vertex(ZXType::ZSpider, qt);
157
1/2
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
6 rep.add_wire(zph, tri, ZXWireType::Basic, qt, std::nullopt, 0);
158
1/2
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
6 rep.add_wire(tri, one, ZXWireType::Basic, qt, 1, std::nullopt);
159 }
160 // Identify alpha s.t. r = e^{i*pi*alpha} + e^{-i*pi*alpha} =
161 // 2*cos(alpha) and implement the algebraic Zbox
162 2 double alpha = std::acos(r / 2.);
163
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 ZXVert tri = rep.add_vertex(ZXType::Triangle, qt);
164
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 ZXVert negal = rep.add_vertex(ZXType::ZSpider, Expr(-alpha), qt);
165
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 ZXVert xmerge = rep.add_vertex(ZXType::XSpider, qt);
166
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 ZXVert al = rep.add_vertex(ZXType::ZSpider, Expr(alpha), qt);
167
1/2
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 rep.add_wire(zph, tri, ZXWireType::Basic, qt, std::nullopt, 0);
168
1/2
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 rep.add_wire(tri, negal, ZXWireType::Basic, qt, 1, std::nullopt);
169
1/2
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 rep.add_wire(zph, xmerge, ZXWireType::Basic, qt);
170
1/2
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 rep.add_wire(negal, xmerge, ZXWireType::Basic, qt);
171
1/2
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 rep.add_wire(xmerge, al, ZXWireType::Basic, qt);
172
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 rep.multiply_scalar(
173
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
6 (qt == QuantumType::Quantum) ? Expr(2.)
174
4/12
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
4 : Expr(SymEngine::sqrt(Expr(2.))));
175
176 // Decompose triangles
177
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 rebase_to_zx_fun(rep);
178
179 // Substitute
180
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 ZXDiagram::Subdiagram sub{v_bounds, {v}};
181
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 diag.substitute(rep, sub);
182 2 break;
183 4 }
184
1/1
✓ Decision 'true' taken 1 times.
1 case ZXType::XY: {
185
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 ZXGen_ptr vgen = diag.get_vertex_ZXGen_ptr(v);
186 1 const PhasedGen& vg = static_cast<const PhasedGen&>(*vgen);
187 ZXGen_ptr new_gen = ZXGen::create_gen(
188
4/8
✓ 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.
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
2 ZXType::ZSpider, -vg.get_param(), *vg.get_qtype());
189
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 diag.set_vertex_ZXGen_ptr(v, new_gen);
190 1 break;
191 1 }
192
1/1
✓ Decision 'true' taken 1 times.
1 case ZXType::XZ: {
193
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 ZXGen_ptr vgen = diag.get_vertex_ZXGen_ptr(v);
194 1 const PhasedGen& vg = static_cast<const PhasedGen&>(*vgen);
195 ZXVert phase_v =
196
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.
1 diag.add_vertex(ZXType::XSpider, vg.get_param(), *vg.get_qtype());
197 ZXGen_ptr new_gen =
198
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.
1 ZXGen::create_gen(ZXType::ZSpider, Expr(0.5), *vg.get_qtype());
199
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 diag.set_vertex_ZXGen_ptr(v, new_gen);
200
2/4
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
1 diag.add_wire(v, phase_v, ZXWireType::Basic, *vg.get_qtype());
201 1 break;
202 1 }
203
1/1
✓ Decision 'true' taken 1 times.
1 case ZXType::YZ: {
204
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 ZXGen_ptr vgen = diag.get_vertex_ZXGen_ptr(v);
205 1 const PhasedGen& vg = static_cast<const PhasedGen&>(*vgen);
206 ZXVert phase_v =
207
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.
1 diag.add_vertex(ZXType::XSpider, vg.get_param(), *vg.get_qtype());
208
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
1 ZXGen_ptr new_gen = ZXGen::create_gen(ZXType::ZSpider, *vg.get_qtype());
209
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 diag.set_vertex_ZXGen_ptr(v, new_gen);
210
2/4
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
1 diag.add_wire(v, phase_v, ZXWireType::Basic, *vg.get_qtype());
211 1 break;
212 1 }
213
1/1
✓ Decision 'true' taken 1 times.
1 case ZXType::PX: {
214
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 ZXGen_ptr vgen = diag.get_vertex_ZXGen_ptr(v);
215 1 const CliffordGen& vg = static_cast<const CliffordGen&>(*vgen);
216 ZXGen_ptr new_gen = ZXGen::create_gen(
217
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
1 ZXType::ZSpider, vg.get_param() ? Expr(1.) : Expr(0.),
218
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 *vg.get_qtype());
219
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 diag.set_vertex_ZXGen_ptr(v, new_gen);
220 1 break;
221 1 }
222
1/1
✓ Decision 'true' taken 1 times.
1 case ZXType::PY: {
223
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 ZXGen_ptr vgen = diag.get_vertex_ZXGen_ptr(v);
224 1 const CliffordGen& vg = static_cast<const CliffordGen&>(*vgen);
225 ZXGen_ptr new_gen = ZXGen::create_gen(
226
2/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
1 ZXType::ZSpider, vg.get_param() ? Expr(0.5) : Expr(-0.5),
227
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 *vg.get_qtype());
228
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 diag.set_vertex_ZXGen_ptr(v, new_gen);
229 1 break;
230 1 }
231
1/1
✓ Decision 'true' taken 1 times.
1 case ZXType::PZ: {
232
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 ZXGen_ptr vgen = diag.get_vertex_ZXGen_ptr(v);
233 1 const CliffordGen& vg = static_cast<const CliffordGen&>(*vgen);
234
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 ZXVert phase_v = diag.add_vertex(
235
3/8
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
1 ZXType::XSpider, vg.get_param() ? Expr(1.) : Expr(0.),
236
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 *vg.get_qtype());
237
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
1 ZXGen_ptr new_gen = ZXGen::create_gen(ZXType::ZSpider, *vg.get_qtype());
238
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 diag.set_vertex_ZXGen_ptr(v, new_gen);
239
2/4
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
1 diag.add_wire(v, phase_v, ZXWireType::Basic, *vg.get_qtype());
240 1 break;
241 1 }
242
0/1
✗ Decision 'true' not taken.
16 case ZXType::Triangle: {
243 // Decomposition of the triangle given in Lemma 3.3, "Completeness of
244 // the ZX-calculus for Pure Qubit Clifford+T Quantum Mechanics", K. Feng
245 // Ng, Q. Wang, 2018
246
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 ZXDiagram tri(0, 0, 0, 0);
247
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 QuantumType qt = *diag.get_qtype(v);
248
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 ZXVert in = tri.add_vertex(ZXType::Input, qt);
249
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 tri.boundary.push_back(in);
250
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 ZXVert out = tri.add_vertex(ZXType::Output, qt);
251
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 tri.boundary.push_back(out);
252
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 tri.multiply_scalar(
253
4/6
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 12 times.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 12 times.
✗ Branch 7 not taken.
44 (qt == QuantumType::Quantum) ? Expr(2.)
254
6/12
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 12 times.
✓ Branch 8 taken 4 times.
✓ Branch 10 taken 12 times.
✓ Branch 11 taken 4 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
28 : Expr(SymEngine::sqrt(Expr(2.))));
255
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 ZXVert split = tri.add_vertex(ZXType::XSpider, qt);
256
2/4
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
16 ZXVert lrz = tri.add_vertex(ZXType::ZSpider, -0.25, qt);
257
2/4
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
16 ZXVert rrz = tri.add_vertex(ZXType::ZSpider, 0.25, qt);
258
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 ZXVert laxis = tri.add_vertex(ZXType::XSpider, qt);
259
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 ZXVert raxis = tri.add_vertex(ZXType::XSpider, qt);
260
2/4
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
16 ZXVert lph = tri.add_vertex(ZXType::ZSpider, -0.25, qt);
261
2/4
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
16 ZXVert rph = tri.add_vertex(ZXType::ZSpider, 0.25, qt);
262
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 ZXVert merge = tri.add_vertex(ZXType::ZSpider, qt);
263 // Flat of the triangle is the output/port 1
264
1/2
✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
16 tri.add_wire(out, split, ZXWireType::Basic, qt);
265
1/2
✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
16 tri.add_wire(split, lrz, ZXWireType::Basic, qt);
266
1/2
✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
16 tri.add_wire(split, rrz, ZXWireType::Basic, qt);
267
1/2
✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
16 tri.add_wire(lrz, laxis, ZXWireType::Basic, qt);
268
1/2
✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
16 tri.add_wire(rrz, raxis, ZXWireType::Basic, qt);
269
1/2
✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
16 tri.add_wire(laxis, lph, ZXWireType::Basic, qt);
270
1/2
✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
16 tri.add_wire(raxis, rph, ZXWireType::Basic, qt);
271
1/2
✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
16 tri.add_wire(laxis, merge, ZXWireType::Basic, qt);
272
1/2
✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
16 tri.add_wire(raxis, merge, ZXWireType::Basic, qt);
273 // Point of the triangle is the input/port 0
274
1/2
✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
16 tri.add_wire(merge, in, ZXWireType::Basic, qt);
275
276
1/2
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
16 Wire w0 = diag.wire_at_port(v, 0);
277
1/2
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
16 Wire w1 = diag.wire_at_port(v, 1);
278 WireEnd we0, we1;
279
2/4
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 16 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 16 times.
16 if (w0 == w1) {
280
0/2
✗ Decision 'true' not taken.
✗ Decision 'false' not taken.
if (diag.source_port(w0) == 0) {
281 we0 = WireEnd::Source;
282 we1 = WireEnd::Target;
283 } else {
284 we0 = WireEnd::Target;
285 we1 = WireEnd::Source;
286 }
287 } else {
288
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 we0 = diag.end_of(w0, v);
289
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 we1 = diag.end_of(w1, v);
290 }
291
4/8
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 16 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 16 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 16 times.
✗ Branch 15 not taken.
32 ZXDiagram::Subdiagram sub{{{w0, we0}, {w1, we1}}, {v}};
292
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 diag.substitute(tri, sub);
293 16 break;
294 16 }
295
0/1
✗ Decision 'true' not taken.
default:
296 break;
297 }
298 }
299 10 return !verts.empty();
300 5 }
301
302
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 Rewrite Rewrite::rebase_to_zx() { return Rewrite(rebase_to_zx_fun); }
303
304 3 bool Rewrite::rebase_to_mbqc_fun(ZXDiagram& diag) {
305 3 ZXVertVec verts;
306
7/8
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 91 times.
✓ Branch 7 taken 3 times.
✓ Branch 9 taken 91 times.
✓ Branch 10 taken 3 times.
✓ Branch 12 taken 3 times.
✓ Branch 13 taken 3 times.
97 BGL_FORALL_VERTICES(v, *diag.graph, ZXGraph) {
307
1/2
✓ Branch 1 taken 91 times.
✗ Branch 2 not taken.
91 ZXType t = diag.get_zxtype(v);
308
9/12
✓ Branch 1 taken 91 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 81 times.
✓ Branch 4 taken 10 times.
✓ Branch 6 taken 81 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 75 times.
✓ Branch 9 taken 6 times.
✓ Branch 10 taken 75 times.
✓ Branch 11 taken 16 times.
✓ Branch 13 taken 75 times.
✗ Branch 14 not taken.
2/2
✓ Decision 'true' taken 78 times.
✓ Decision 'false' taken 13 times.
91 if (!is_boundary_type(t) && !is_MBQC_type(t)) verts.push_back(v);
309 }
310
2/2
✓ Branch 5 taken 75 times.
✓ Branch 6 taken 3 times.
2/2
✓ Decision 'true' taken 75 times.
✓ Decision 'false' taken 3 times.
78 for (const ZXVert& v : verts) {
311
4/7
✓ Branch 1 taken 75 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 47 times.
✓ Branch 4 taken 26 times.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
75 switch (diag.get_zxtype(v)) {
312
1/1
✓ Decision 'true' taken 47 times.
47 case ZXType::ZSpider: {
313
1/2
✓ Branch 1 taken 47 times.
✗ Branch 2 not taken.
47 ZXGen_ptr vgen = diag.get_vertex_ZXGen_ptr(v);
314 47 const PhasedGen& vg = static_cast<const PhasedGen&>(*vgen);
315 ZXGen_ptr new_gen =
316
4/8
✓ Branch 1 taken 47 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 47 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 47 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 47 times.
✗ Branch 12 not taken.
94 ZXGen::create_gen(ZXType::XY, -vg.get_param(), *vg.get_qtype());
317
1/2
✓ Branch 1 taken 47 times.
✗ Branch 2 not taken.
47 diag.set_vertex_ZXGen_ptr(v, new_gen);
318 47 break;
319 47 }
320
1/1
✓ Decision 'true' taken 26 times.
26 case ZXType::XSpider: {
321
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 ZXGen_ptr vgen = diag.get_vertex_ZXGen_ptr(v);
322 26 const PhasedGen& vg = static_cast<const PhasedGen&>(*vgen);
323 ZXGen_ptr new_gen =
324
4/8
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 26 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 26 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 26 times.
✗ Branch 12 not taken.
52 ZXGen::create_gen(ZXType::XY, -vg.get_param(), *vg.get_qtype());
325
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 diag.set_vertex_ZXGen_ptr(v, new_gen);
326
3/4
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
✓ Branch 8 taken 77 times.
✓ Branch 9 taken 26 times.
0/1
? Decision couldn't be analyzed.
103 for (const Wire& w : diag.adj_wires(v)) {
327
1/2
✓ Branch 1 taken 77 times.
✗ Branch 2 not taken.
77 ZXWireType wt = diag.get_wire_type(w);
328
2/4
✓ Branch 0 taken 77 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 77 times.
✗ Branch 4 not taken.
77 diag.set_wire_type(
329 w, (wt == ZXWireType::Basic) ? ZXWireType::H : ZXWireType::Basic);
330 26 }
331 26 break;
332 26 }
333
1/1
✓ Decision 'true' taken 2 times.
2 case ZXType::Hbox: {
334
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 WireVec v_ws = diag.adj_wires(v);
335 2 std::vector<std::pair<Wire, WireEnd>> v_bounds;
336
2/2
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 2 times.
2/2
✓ Decision 'true' taken 5 times.
✓ Decision 'false' taken 2 times.
7 for (const Wire& w : v_ws) {
337
4/6
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 2 times.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
1/2
✓ Decision 'true' taken 5 times.
✗ Decision 'false' not taken.
5 if (diag.source(w) == v) v_bounds.push_back({w, WireEnd::Source});
338
4/6
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 2 times.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
2/2
✓ Decision 'true' taken 4 times.
✓ Decision 'false' taken 1 times.
5 if (diag.target(w) == v) v_bounds.push_back({w, WireEnd::Target});
339 }
340
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 ZXDiagram::Subdiagram sub{v_bounds, {v}};
341
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 ZXDiagram h = sub.to_diagram(diag);
342
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 rebase_to_zx_fun(h);
343
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 rebase_to_mbqc_fun(h);
344
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 diag.substitute(h, sub);
345 2 break;
346 2 }
347
0/1
✗ Decision 'true' not taken.
case ZXType::Triangle: {
348 Wire w0 = diag.wire_at_port(v, 0);
349 Wire w1 = diag.wire_at_port(v, 1);
350 WireEnd we0, we1;
351
0/2
✗ Decision 'true' not taken.
✗ Decision 'false' not taken.
if (w0 == w1) {
352
0/2
✗ Decision 'true' not taken.
✗ Decision 'false' not taken.
if (diag.source_port(w0) == 0) {
353 we0 = WireEnd::Source;
354 we1 = WireEnd::Target;
355 } else {
356 we0 = WireEnd::Target;
357 we1 = WireEnd::Source;
358 }
359 } else {
360 we0 = diag.end_of(w0, v);
361 we1 = diag.end_of(w1, v);
362 }
363 ZXDiagram::Subdiagram sub{{{w0, we0}, {w1, we1}}, {v}};
364 ZXDiagram tri = sub.to_diagram(diag);
365 rebase_to_zx_fun(tri);
366 rebase_to_mbqc_fun(tri);
367 diag.substitute(tri, sub);
368 break;
369 }
370
0/1
✗ Decision 'true' not taken.
default:
371 break;
372 }
373 }
374 6 return !verts.empty();
375 3 }
376
377
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 Rewrite Rewrite::rebase_to_mbqc() { return Rewrite(rebase_to_mbqc_fun); }
378
379 } // namespace zx
380
381 } // namespace tket
382