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 "GateUnitarySparseMatrix.hpp" | |||
16 | ||||
17 | #include <tkassert/Assert.hpp> | |||
18 | ||||
19 | #include "Gate/Gate.hpp" | |||
20 | #include "GateUnitaryMatrix.hpp" | |||
21 | #include "GateUnitaryMatrixError.hpp" | |||
22 | #include "GateUnitaryMatrixImplementations.hpp" | |||
23 | #include "GateUnitaryMatrixUtils.hpp" | |||
24 | ||||
25 | namespace tket { | |||
26 | namespace internal { | |||
27 | ||||
28 | // Given a controlled type, which can be written as | |||
29 | // a number of control qubits applied to a more primitive type, | |||
30 | // return the primitive type. (e.g. CnX returns X). | |||
31 | // Returns "noop" if it's not a controlled type we know how to deal with. | |||
32 | // We are excluding things like, e.g., CX, which are only 4x4 and too small | |||
33 | // to be worth dealing with specially. | |||
34 | 191456 | static OpType get_primitive_type(OpType type_without_controls) { | ||
35 |
5/5✓ Branch 0 taken 93 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 25 times.
✓ Branch 4 taken 191322 times.
|
191456 | switch (type_without_controls) { | |
36 |
0/1✗ Decision 'true' not taken.
|
93 | case OpType::CnX: | |
37 | // fall through | |||
38 | case OpType::CCX: | |||
39 | 93 | return OpType::X; | ||
40 | ||||
41 |
1/1✓ Decision 'true' taken 8 times.
|
8 | case OpType::CnZ: | |
42 | 8 | return OpType::Z; | ||
43 | ||||
44 |
1/1✓ Decision 'true' taken 8 times.
|
8 | case OpType::CnY: | |
45 | 8 | return OpType::Y; | ||
46 | ||||
47 |
1/1✓ Decision 'true' taken 25 times.
|
25 | case OpType::CnRy: | |
48 | 25 | return OpType::Ry; | ||
49 | ||||
50 |
1/1✓ Decision 'true' taken 191322 times.
|
191322 | default: | |
51 | 191322 | return OpType::noop; | ||
52 | } | |||
53 | } | |||
54 | ||||
55 | // We have a type acting on 1 qubit. | |||
56 | // Convert this to a type acting on n qubits (given by the Gate object) | |||
57 | // by adding controls. | |||
58 | static std::vector<TripletCd> | |||
59 | 134 | convert_1qb_type_to_controlled_type_and_get_triplets( | ||
60 | const Gate& gate, OpType one_qubit_type, double abs_epsilon) { | |||
61 |
2/4✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 134 times.
✗ Branch 5 not taken.
|
134 | const Gate new_gate(one_qubit_type, gate.get_params(), 1); | |
62 |
1/2✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
|
134 | const auto small_unitary = GateUnitaryMatrix::get_unitary(new_gate); | |
63 |
1/2✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
|
134 | auto triplets = get_triplets(small_unitary, abs_epsilon); | |
64 | ||||
65 | // e.g., if CnX or CnRy for n=3, then U is 2x2, but we are embedding into | |||
66 | // the bottom right corner of an 8x8 identity matrix | |||
67 |
2/4✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 134 times.
✗ Branch 5 not taken.
|
134 | unsigned full_matr_size = get_matrix_size(gate.n_qubits()); | |
68 | 134 | unsigned translation = full_matr_size - small_unitary.rows(); | ||
69 |
1/2✓ Branch 0 taken 134 times.
✗ Branch 1 not taken.
|
0/1? Decision couldn't be analyzed.
|
134 | if (translation > 0) { |
70 |
2/2✓ Branch 4 taken 318 times.
✓ Branch 5 taken 134 times.
|
2/2✓ Decision 'true' taken 318 times.
✓ Decision 'false' taken 134 times.
|
452 | for (auto& triplet : triplets) { |
71 | 318 | triplet = TripletCd( | ||
72 | 318 | triplet.row() + translation, triplet.col() + translation, | ||
73 | triplet.value()); | |||
74 | } | |||
75 |
1/2✓ Branch 2 taken 134 times.
✗ Branch 3 not taken.
|
134 | triplets.reserve(triplets.size() + translation); | |
76 |
2/2✓ Branch 0 taken 1056 times.
✓ Branch 1 taken 134 times.
|
2/2✓ Decision 'true' taken 1056 times.
✓ Decision 'false' taken 134 times.
|
1190 | for (unsigned ii = 0; ii < translation; ++ii) { |
77 |
1/2✓ Branch 1 taken 1056 times.
✗ Branch 2 not taken.
|
1056 | triplets.emplace_back(ii, ii, 1.0); | |
78 | } | |||
79 | } | |||
80 | 268 | return triplets; | ||
81 | 134 | } | ||
82 | ||||
83 | namespace { | |||
84 | struct FixedTripletsWithNoParameters { | |||
85 | std::vector<TripletCd> bridge_triplets; | |||
86 | std::vector<TripletCd> cswap_triplets; | |||
87 | ||||
88 | FixedTripletsWithNoParameters(); | |||
89 | ||||
90 | // It just so happens that these gates all take | |||
91 | // the same number of qubits and parameters. | |||
92 | // Therefore, test the gate for this, as well as | |||
93 | // returning the const data for use. | |||
94 | static const FixedTripletsWithNoParameters& get(const Gate& gate); | |||
95 | }; | |||
96 | ||||
97 | 1 | FixedTripletsWithNoParameters::FixedTripletsWithNoParameters() { | ||
98 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | bridge_triplets.reserve(8); | |
99 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | cswap_triplets.reserve(8); | |
100 | const auto& bridge_cols = | |||
101 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | GateUnitaryMatrixImplementations::get_bridge_columns(); | |
102 | const auto& cswap_cols = | |||
103 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | GateUnitaryMatrixImplementations::get_cswap_columns(); | |
104 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1 times.
|
2/2✓ Decision 'true' taken 8 times.
✓ Decision 'false' taken 1 times.
|
9 | for (unsigned ii = 0; ii < 8; ++ii) { |
105 |
1/2✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | bridge_triplets.emplace_back(ii, bridge_cols[ii], 1.0); | |
106 |
1/2✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | cswap_triplets.emplace_back(ii, cswap_cols[ii], 1.0); | |
107 | } | |||
108 | 1 | } | ||
109 | ||||
110 | 19 | const FixedTripletsWithNoParameters& FixedTripletsWithNoParameters::get( | ||
111 | const Gate& gate) { | |||
112 |
4/8✓ Branch 0 taken 1 times.
✓ Branch 1 taken 18 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
|
19 | static const FixedTripletsWithNoParameters data; | |
113 | ||||
114 |
1/2✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
|
19 | GateUnitaryMatrixUtils::check_and_throw_upon_wrong_number_of_parameters( | |
115 |
1/2✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
|
19 | gate.get_type(), gate.n_qubits(), | |
116 | 38 | GateUnitaryMatrixUtils::get_checked_parameters(gate), 0); | ||
117 | TKET_ASSERT(gate.n_qubits() == 3); | |||
118 | 19 | return data; | ||
119 | } | |||
120 | } // namespace | |||
121 | ||||
122 | 41 | static std::vector<TripletCd> get_phase_gadget_triplets( | ||
123 | unsigned number_of_qubits, double param) { | |||
124 | // All diagonal entries have abs value 1, | |||
125 | // so no point in abs_epsilon. | |||
126 | const auto entries = | |||
127 | GateUnitaryMatrixImplementations::PhaseGadget_diagonal_entries( | |||
128 |
1/2✓ Branch 1 taken 41 times.
✗ Branch 2 not taken.
|
41 | number_of_qubits, param); | |
129 |
1/2✓ Branch 3 taken 41 times.
✗ Branch 4 not taken.
|
41 | std::vector<TripletCd> triplets(entries.rows()); | |
130 |
2/2✓ Branch 1 taken 294 times.
✓ Branch 2 taken 41 times.
|
2/2✓ Decision 'true' taken 294 times.
✓ Decision 'false' taken 41 times.
|
335 | for (unsigned ii = 0; ii < entries.rows(); ++ii) { |
131 |
1/2✓ Branch 1 taken 294 times.
✗ Branch 2 not taken.
|
294 | triplets[ii] = TripletCd(ii, ii, entries(ii)); | |
132 | } | |||
133 | 82 | return triplets; | ||
134 | 41 | } | ||
135 | ||||
136 | 191322 | static std::vector<TripletCd> get_triplets_for_noncontrolled_gate( | ||
137 | const Gate& gate) { | |||
138 |
4/4✓ Branch 1 taken 5 times.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 41 times.
✓ Branch 4 taken 191262 times.
|
191322 | switch (gate.get_type()) { | |
139 |
1/1✓ Decision 'true' taken 5 times.
|
5 | case OpType::CSWAP: | |
140 | 5 | return FixedTripletsWithNoParameters::get(gate).cswap_triplets; | ||
141 |
1/1✓ Decision 'true' taken 14 times.
|
14 | case OpType::BRIDGE: | |
142 | 14 | return FixedTripletsWithNoParameters::get(gate).bridge_triplets; | ||
143 |
1/1✓ Decision 'true' taken 41 times.
|
41 | case OpType::PhaseGadget: { | |
144 |
1/2✓ Branch 1 taken 41 times.
✗ Branch 2 not taken.
|
41 | const auto params = GateUnitaryMatrixUtils::get_checked_parameters(gate); | |
145 |
1/2✓ Branch 2 taken 41 times.
✗ Branch 3 not taken.
|
41 | GateUnitaryMatrixUtils::check_and_throw_upon_wrong_number_of_parameters( | |
146 |
1/2✓ Branch 1 taken 41 times.
✗ Branch 2 not taken.
|
41 | gate.get_type(), gate.n_qubits(), params, 1); | |
147 |
2/4✓ Branch 2 taken 41 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 41 times.
✗ Branch 6 not taken.
|
41 | return get_phase_gadget_triplets(gate.n_qubits(), params[0]); | |
148 | 41 | } | ||
149 |
1/1✓ Decision 'true' taken 191262 times.
|
191262 | default: | |
150 | 191262 | return {}; | ||
151 | } | |||
152 | } | |||
153 | ||||
154 | 191456 | std::vector<TripletCd> GateUnitarySparseMatrix::get_unitary_triplets( | ||
155 | const Gate& gate, double abs_epsilon) { | |||
156 | 191456 | const auto type = gate.get_type(); | ||
157 | 191456 | const auto primitive_type = get_primitive_type(type); | ||
158 |
2/2✓ Branch 0 taken 134 times.
✓ Branch 1 taken 191322 times.
|
2/2✓ Decision 'true' taken 134 times.
✓ Decision 'false' taken 191322 times.
|
191456 | if (primitive_type != OpType::noop) { |
159 | try { | |||
160 | return convert_1qb_type_to_controlled_type_and_get_triplets( | |||
161 |
1/2✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
|
134 | gate, primitive_type, abs_epsilon); | |
162 | ✗ | } catch (const GateUnitaryMatrixError& e) { | ||
163 | // GCOVR_EXCL_START | |||
164 | TKET_ASSERT( | |||
165 | AssertMessage() | |||
166 | << "Converting " << gate.get_name() | |||
167 | << " to sparse unitary, via adding controls to gate type " | |||
168 | << OpDesc(primitive_type).name() << ": " << e.what()); | |||
169 | // GCOVR_EXCL_STOP | |||
170 | } | |||
171 | } | |||
172 | 191322 | return get_triplets_for_noncontrolled_gate(gate); | ||
173 | } | |||
174 | ||||
175 | } // namespace internal | |||
176 | } // namespace tket | |||
177 |