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 |