GCC Code Coverage Report


Directory: ./
File: Simulation/DecomposeCircuit.cpp
Date: 2022-10-15 05:10:18
Warnings: 2 unchecked decisions!
Exec Total Coverage
Lines: 85 100 85.0%
Functions: 6 7 85.7%
Branches: 87 184 47.3%
Decisions: 8 14 57.1%

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 "DecomposeCircuit.hpp"
16
17 #include <sstream>
18 #include <tkassert/Assert.hpp>
19
20 #include "Circuit/Boxes.hpp"
21 #include "Circuit/Circuit.hpp"
22 #include "Gate/Gate.hpp"
23 #include "Gate/GateUnitaryMatrix.hpp"
24 #include "Gate/GateUnitaryMatrixError.hpp"
25 #include "GateNodesBuffer.hpp"
26 #include "PauliExpBoxUnitaryCalculator.hpp"
27
28 namespace tket {
29 namespace tket_sim {
30 namespace internal {
31
32 typedef std::map<Qubit, unsigned> QMap;
33
34 // No checks because only used internally
35 5955 static QMap get_qmap_no_checks(
36 const Circuit& circ,
37 const std::vector<unsigned>& parent_circuit_qubit_indices) {
38 5955 std::map<Qubit, unsigned> qmap;
39 5955 unsigned index = 0;
40
3/4
✓ Branch 1 taken 5955 times.
✗ Branch 2 not taken.
✓ Branch 7 taken 16796 times.
✓ Branch 8 taken 5955 times.
0/1
? Decision couldn't be analyzed.
22751 for (const Qubit& q : circ.all_qubits()) {
41 TKET_ASSERT(qmap.count(q) == 0);
42
1/2
✓ Branch 2 taken 16796 times.
✗ Branch 3 not taken.
16796 qmap[q] = parent_circuit_qubit_indices[index];
43 16796 ++index;
44 5955 }
45 TKET_ASSERT(qmap.size() <= parent_circuit_qubit_indices.size());
46 5955 return qmap;
47 }
48
49 // Already known to be a box, with a nonempty Op ptr.
50 // If possible (if the op is able to calculate its own unitary matrix),
51 // fill the node triplets with the raw unitary matrix represented by this box.
52 // Return true if it found the triplets.
53 251 static bool fill_triplets_directly_from_box(
54 GateNode& node, const std::shared_ptr<const Box>& box_ptr, OpType type,
55 double abs_epsilon) {
56
5/6
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 132 times.
✓ Branch 4 taken 21 times.
✓ Branch 5 taken 84 times.
251 switch (type) {
57
1/1
✓ Decision 'true' taken 2 times.
2 case OpType::Unitary1qBox: {
58
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 auto u1q_ptr = dynamic_cast<const Unitary1qBox*>(box_ptr.get());
59 TKET_ASSERT(u1q_ptr);
60
2/4
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
2 node.triplets = tket::get_triplets(u1q_ptr->get_matrix(), abs_epsilon);
61 2 return true;
62 }
63
1/1
✓ Decision 'true' taken 12 times.
12 case OpType::Unitary2qBox: {
64
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 auto u2q_ptr = dynamic_cast<const Unitary2qBox*>(box_ptr.get());
65 TKET_ASSERT(u2q_ptr);
66
2/4
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
12 node.triplets = tket::get_triplets(u2q_ptr->get_matrix(), abs_epsilon);
67 12 return true;
68 }
69
0/1
✗ Decision 'true' not taken.
case OpType::Unitary3qBox: {
70 auto u3q_ptr = dynamic_cast<const Unitary3qBox*>(box_ptr.get());
71 TKET_ASSERT(u3q_ptr);
72 node.triplets = tket::get_triplets(u3q_ptr->get_matrix(), abs_epsilon);
73 return true;
74 }
75
1/1
✓ Decision 'true' taken 132 times.
132 case OpType::PauliExpBox: {
76
1/2
✓ Branch 1 taken 132 times.
✗ Branch 2 not taken.
132 auto pauli_box_ptr = dynamic_cast<const PauliExpBox*>(box_ptr.get());
77 TKET_ASSERT(pauli_box_ptr);
78 132 node.triplets = get_triplets(*pauli_box_ptr);
79 132 return true;
80 }
81
1/1
✓ Decision 'true' taken 21 times.
21 case OpType::ExpBox: {
82
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
21 auto exp_box_ptr = dynamic_cast<const ExpBox*>(box_ptr.get());
83 TKET_ASSERT(exp_box_ptr);
84
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
21 const auto numerical_data = exp_box_ptr->get_matrix_and_phase();
85 Eigen::Matrix4cd matrix =
86
2/4
✓ Branch 2 taken 21 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 21 times.
✗ Branch 6 not taken.
21 numerical_data.first * (i_ * numerical_data.second);
87
2/4
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 21 times.
✗ Branch 5 not taken.
21 matrix = matrix.exp();
88
2/4
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 21 times.
✗ Branch 5 not taken.
21 node.triplets = tket::get_triplets(matrix);
89 21 return true;
90 }
91
1/1
✓ Decision 'true' taken 84 times.
84 default:
92 84 return false;
93 }
94 }
95
96 5952 static void add_global_phase(const Circuit& circ, GateNodesBuffer& buffer) {
97
2/4
✓ Branch 1 taken 5952 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5952 times.
✗ Branch 5 not taken.
5952 const auto global_phase = eval_expr(circ.get_phase());
98
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5952 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 5952 times.
5952 if (!global_phase) {
99 throw SymbolsNotSupported("Circuit has symbolic global phase");
100 }
101
1/2
✓ Branch 1 taken 5952 times.
✗ Branch 2 not taken.
5952 const double phase_value = global_phase.value();
102
1/2
✓ Branch 1 taken 5952 times.
✗ Branch 2 not taken.
5952 buffer.add_global_phase(phase_value);
103 5952 }
104
105 static void throw_with_op_error_message(
106 const std::string& op_name, const QMap& qmap, const Circuit& circ,
107 const std::string& extra_message) {
108 std::stringstream ss;
109 ss << "Subcircuit\n"
110 << circ << "\nwith " << qmap.size() << " qubits, has op " << op_name
111 << ". " << extra_message;
112 throw CircuitInvalidity(ss.str());
113 }
114
115 191121 static void fill_qubit_indices(
116 const unit_vector_t& args, const QMap& qmap, GateNode& node) {
117 TKET_ASSERT(args.size() <= qmap.size());
118 191121 node.qubit_indices.resize(args.size());
119
2/2
✓ Branch 1 taken 275288 times.
✓ Branch 2 taken 191121 times.
2/2
✓ Decision 'true' taken 275288 times.
✓ Decision 'false' taken 191121 times.
466409 for (unsigned ii = 0; ii < args.size(); ++ii) {
120
1/2
✓ Branch 3 taken 275288 times.
✗ Branch 4 not taken.
275288 node.qubit_indices[ii] = qmap.at(Qubit(args[ii]));
121 }
122 191121 }
123
124 5955 static void decompose_circuit_recursive(
125 const Circuit& circ, GateNodesBuffer& buffer,
126 const std::vector<unsigned>& parent_circuit_qubit_indices,
127 double abs_epsilon) {
128
1/2
✓ Branch 1 taken 5955 times.
✗ Branch 2 not taken.
5955 const auto qmap = get_qmap_no_checks(circ, parent_circuit_qubit_indices);
129 5955 unit_vector_t args;
130 5955 GateNode node;
131
132
6/10
✓ Branch 1 taken 5955 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5955 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 191131 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 191128 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 191131 times.
✓ Branch 14 taken 5952 times.
0/1
? Decision couldn't be analyzed.
197083 for (const Command& command : circ) {
133 191131 const Op_ptr current_op = command.get_op_ptr();
134 TKET_ASSERT(current_op.get());
135 191131 const OpType current_type = current_op->get_type();
136
9/12
✓ Branch 1 taken 191131 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 191131 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 191131 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 191129 times.
✓ Branch 9 taken 2 times.
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 191128 times.
✓ Branch 12 taken 3 times.
✓ Branch 13 taken 191128 times.
191131 if (is_classical_type(current_type) || is_projective_type(current_type) ||
137 current_type == OpType::Conditional) {
138
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 throw GateUnitaryMatrixError(
139
2/4
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
6 "Unsupported OpType " + current_op->get_name(),
140 6 GateUnitaryMatrixError::Cause::GATE_NOT_IMPLEMENTED);
141 }
142
4/4
✓ Branch 0 taken 191123 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 191121 times.
191128 if (current_type == OpType::noop || current_type == OpType::Barrier) {
143 7 continue;
144 }
145
1/2
✓ Branch 2 taken 191121 times.
✗ Branch 3 not taken.
191121 const OpDesc desc = current_op->get_desc();
146
1/2
✓ Branch 1 taken 191121 times.
✗ Branch 2 not taken.
191121 args = command.get_args();
147
1/2
✓ Branch 1 taken 191121 times.
✗ Branch 2 not taken.
191121 fill_qubit_indices(args, qmap, node);
148
3/4
✓ Branch 1 taken 191121 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 190870 times.
✓ Branch 4 taken 251 times.
191121 if (desc.is_gate()) {
149
1/2
✓ Branch 1 taken 190870 times.
✗ Branch 2 not taken.
190870 const Gate* gate = dynamic_cast<const Gate*>(current_op.get());
150 TKET_ASSERT(gate);
151 node.triplets =
152
1/2
✓ Branch 1 taken 190870 times.
✗ Branch 2 not taken.
190870 GateUnitaryMatrix::get_unitary_triplets(*gate, abs_epsilon);
153
1/2
✓ Branch 1 taken 190870 times.
✗ Branch 2 not taken.
190870 buffer.push(node);
154 190870 continue;
155 190870 }
156
2/4
✓ Branch 1 taken 251 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 251 times.
251 if (!desc.is_box()) {
157 throw_with_op_error_message(
158 desc.name(), qmap, circ, "This is not a gate or box type.");
159 }
160 std::shared_ptr<const Box> box_ptr =
161 251 std::dynamic_pointer_cast<const Box>(current_op);
162 TKET_ASSERT(box_ptr.get());
163
164 251 node.triplets.clear();
165
1/2
✓ Branch 3 taken 251 times.
✗ Branch 4 not taken.
251 const bool found_unitary = fill_triplets_directly_from_box(
166 node, box_ptr, current_op->get_type(), abs_epsilon);
167
168
2/2
✓ Branch 1 taken 167 times.
✓ Branch 2 taken 84 times.
251 if (!node.triplets.empty()) {
169 TKET_ASSERT(found_unitary);
170
1/2
✓ Branch 1 taken 167 times.
✗ Branch 2 not taken.
167 buffer.push(node);
171 167 continue;
172 167 }
173 TKET_ASSERT(!found_unitary);
174 // Break this box down, recursively.
175
1/2
✓ Branch 2 taken 84 times.
✗ Branch 3 not taken.
84 std::shared_ptr<Circuit> box_circ = box_ptr->to_circuit();
176
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 84 times.
84 if (!box_circ.get()) {
177 throw_with_op_error_message(
178 desc.name(), qmap, circ,
179 "This is a box, which couldn't be "
180 "broken down into a circuit");
181 }
182
1/2
✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
84 decompose_circuit_recursive(
183 84 *box_circ, buffer, node.qubit_indices, abs_epsilon);
184
8/8
✓ Branch 2 taken 84 times.
✓ Branch 3 taken 167 times.
✓ Branch 5 taken 84 times.
✓ Branch 6 taken 191037 times.
✓ Branch 8 taken 84 times.
✓ Branch 9 taken 191044 times.
✓ Branch 11 taken 84 times.
✓ Branch 12 taken 191044 times.
579340 }
185
1/2
✓ Branch 1 taken 5952 times.
✗ Branch 2 not taken.
5952 add_global_phase(circ, buffer);
186 5961 }
187
188 5871 void decompose_circuit(
189 const Circuit& circ, GateNodesBuffer& buffer, double abs_epsilon) {
190 // The qubits are just [0,1,2,...].
191
2/4
✓ Branch 2 taken 5871 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 5871 times.
✗ Branch 6 not taken.
5871 std::vector<unsigned> iota(circ.n_qubits());
192 5871 std::iota(iota.begin(), iota.end(), 0);
193
194
2/2
✓ Branch 1 taken 5868 times.
✓ Branch 2 taken 3 times.
5871 decompose_circuit_recursive(circ, buffer, iota, abs_epsilon);
195
1/2
✓ Branch 1 taken 5868 times.
✗ Branch 2 not taken.
5868 buffer.flush();
196 5871 }
197
198 } // namespace internal
199 } // namespace tket_sim
200 } // namespace tket
201