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 "GateUnitaryMatrixUtils.hpp" | |||
16 | ||||
17 | #include <sstream> | |||
18 | ||||
19 | #include "Gate/Gate.hpp" | |||
20 | #include "GateUnitaryMatrixError.hpp" | |||
21 | ||||
22 | namespace tket { | |||
23 | namespace internal { | |||
24 | ||||
25 | 8375 | Eigen::Matrix4cd GateUnitaryMatrixUtils::get_controlled_gate_unitary( | ||
26 | const Eigen::Matrix2cd& u) { | |||
27 |
1/2✓ Branch 2 taken 8375 times.
✗ Branch 3 not taken.
|
8375 | Eigen::Matrix4cd matr = Eigen::Matrix4cd::Identity(); | |
28 |
1/2✓ Branch 2 taken 8375 times.
✗ Branch 3 not taken.
|
8375 | matr.block(2, 2, 2, 2) = u; | |
29 | 8375 | return matr; | ||
30 | } | |||
31 | ||||
32 | Eigen::MatrixXcd | |||
33 | 86 | GateUnitaryMatrixUtils::get_multi_controlled_gate_dense_unitary( | ||
34 | const Eigen::MatrixXcd& u, unsigned int number_of_qubits) { | |||
35 | unsigned int size; | |||
36 | try { | |||
37 |
1/2✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
|
86 | size = get_matrix_size(number_of_qubits); | |
38 | ✗ | } catch (const std::exception& e) { | ||
39 | ✗ | throw GateUnitaryMatrixError( | ||
40 | ✗ | e.what(), GateUnitaryMatrixError::Cause::TOO_MANY_QUBITS); | ||
41 | } | |||
42 | 8 | const auto throw_with_message = [&](const std::string& message) { | ||
43 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | std::stringstream ss; | |
44 |
2/4✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
|
8 | ss << "multi_controlled_gate with " << number_of_qubits | |
45 | << " qubits " | |||
46 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | "(final matrix size " | |
47 |
3/6✓ 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.
|
8 | << size << "x" << size | |
48 | << "), " | |||
49 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | "for unitary matrix U with " | |
50 |
5/10✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 8 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 8 times.
✗ Branch 13 not taken.
✓ Branch 15 taken 8 times.
✗ Branch 16 not taken.
|
8 | << u.cols() << " cols, " << u.rows() << ": " << message; | |
51 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | throw GateUnitaryMatrixError( | |
52 |
1/2✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
16 | ss.str(), GateUnitaryMatrixError::Cause::INPUT_ERROR); | |
53 | 8 | }; | ||
54 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 86 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 86 times.
|
86 | if (u.cols() != u.rows()) { |
55 | ✗ | throw_with_message("matrix U not square"); | ||
56 | } | |||
57 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 86 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 86 times.
|
86 | if (u.cols() == 0) { |
58 | ✗ | throw_with_message("zero size matrix U"); | ||
59 | } | |||
60 |
3/4✓ Branch 0 taken 78 times.
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 78 times.
|
2/2✓ Decision 'true' taken 24 times.
✓ Decision 'false' taken 62 times.
|
86 | if (!(number_of_qubits >= 1 && size >= 2)) { |
61 |
2/4✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 8 times.
|
24 | throw_with_message("must have at least 1 qubit"); | |
62 | } | |||
63 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 78 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 78 times.
|
78 | if (!(size >= u.cols())) { |
64 |
0/1? Decision couldn't be analyzed.
|
✗ | throw_with_message("input U is too large for the final number of qubits"); | |
65 | } | |||
66 | // A trick: we should check that U is of size 2^k * 2^k. | |||
67 | // But actually, we know the full size is 2^N * 2^N for N >= k, | |||
68 | // so we just check for factors. | |||
69 | 78 | const unsigned int ratio = size / u.cols(); | ||
70 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 78 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 78 times.
|
78 | if (ratio * u.cols() != size) { |
71 | ✗ | std::stringstream ss; | ||
72 | ✗ | ss << "input U number of columns is not a power of 2 (" << u.cols() | ||
73 | ✗ | << " doesn't divide " << size << ")"; | ||
74 | ✗ | throw_with_message(ss.str()); | ||
75 | } | |||
76 |
2/4✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 78 times.
✗ Branch 5 not taken.
|
78 | Eigen::MatrixXcd matr = Eigen::MatrixXcd::Identity(size, size); | |
77 |
2/4✓ Branch 5 taken 78 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 78 times.
✗ Branch 9 not taken.
|
78 | matr.block(size - u.cols(), size - u.cols(), u.cols(), u.cols()) = u; | |
78 | 156 | return matr; | ||
79 | } | |||
80 | ||||
81 | 2198 | std::string GateUnitaryMatrixUtils::get_error_prefix( | ||
82 | const std::string& name, unsigned number_of_qubits, | |||
83 | const std::vector<double>& parameters) { | |||
84 |
1/2✓ Branch 1 taken 2198 times.
✗ Branch 2 not taken.
|
2198 | std::stringstream ss; | |
85 |
4/8✓ Branch 1 taken 2198 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2198 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2198 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2198 times.
✗ Branch 11 not taken.
|
0/1? Decision couldn't be analyzed.
|
2198 | ss << "GateUnitaryMatrix for op " << name << " acting on " << number_of_qubits |
86 |
3/6✓ Branch 1 taken 2198 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 2198 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 2198 times.
✗ Branch 9 not taken.
|
2198 | << " qubits, taking " << parameters.size() << " parameters:\n"; | |
87 | ||||
88 |
2/2✓ Branch 1 taken 5678 times.
✓ Branch 2 taken 2198 times.
|
2/2✓ Decision 'true' taken 5678 times.
✓ Decision 'false' taken 2198 times.
|
7876 | for (unsigned nn = 0; nn < parameters.size(); ++nn) { |
89 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5678 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 5678 times.
|
5678 | if (nn >= 10) { |
90 | ✗ | ss << "..."; | ||
91 | ✗ | break; | ||
92 | } | |||
93 |
5/10✓ Branch 1 taken 5678 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5678 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 5678 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 5678 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 5678 times.
✗ Branch 15 not taken.
|
5678 | ss << "param[" << nn << "] = " << parameters[nn] << "\n"; | |
94 | } | |||
95 |
1/2✓ Branch 1 taken 2198 times.
✗ Branch 2 not taken.
|
4396 | return ss.str(); | |
96 | 2198 | } | ||
97 | ||||
98 | 2198 | std::string GateUnitaryMatrixUtils::get_error_prefix( | ||
99 | OpType op_type, unsigned number_of_qubits, | |||
100 | const std::vector<double>& parameters) { | |||
101 |
1/2✓ Branch 1 taken 2198 times.
✗ Branch 2 not taken.
|
2198 | const OpDesc desc(op_type); | |
102 |
2/4✓ Branch 1 taken 2198 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2198 times.
✗ Branch 5 not taken.
|
6594 | return get_error_prefix(desc.name(), number_of_qubits, parameters); | |
103 | 2198 | } | ||
104 | ||||
105 | 196544 | void GateUnitaryMatrixUtils::check_and_throw_upon_wrong_number_of_parameters( | ||
106 | OpType op_type, unsigned number_of_qubits, | |||
107 | const std::vector<double>& parameters, | |||
108 | unsigned expected_number_of_parameters) { | |||
109 |
2/2✓ Branch 1 taken 195014 times.
✓ Branch 2 taken 1530 times.
|
2/2✓ Decision 'true' taken 195014 times.
✓ Decision 'false' taken 1530 times.
|
196544 | if (parameters.size() == expected_number_of_parameters) { |
110 | 195014 | return; | ||
111 | } | |||
112 |
1/2✓ Branch 1 taken 1530 times.
✗ Branch 2 not taken.
|
1530 | std::stringstream ss; | |
113 |
1/2✓ Branch 1 taken 1530 times.
✗ Branch 2 not taken.
|
3060 | ss << get_error_prefix(op_type, number_of_qubits, parameters) | |
114 |
3/6✓ Branch 1 taken 1530 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1530 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1530 times.
✗ Branch 8 not taken.
|
1530 | << "wrong number of parameters (expected " << expected_number_of_parameters | |
115 |
1/2✓ Branch 1 taken 1530 times.
✗ Branch 2 not taken.
|
1530 | << ")"; | |
116 |
1/2✓ Branch 1 taken 1530 times.
✗ Branch 2 not taken.
|
1530 | throw GateUnitaryMatrixError( | |
117 |
1/2✓ Branch 2 taken 1530 times.
✗ Branch 3 not taken.
|
3060 | ss.str(), GateUnitaryMatrixError::Cause::INPUT_ERROR); | |
118 | 1530 | } | ||
119 | ||||
120 | 194434 | std::vector<double> GateUnitaryMatrixUtils::get_checked_parameters( | ||
121 | const Gate& gate) { | |||
122 |
1/2✓ Branch 1 taken 194434 times.
✗ Branch 2 not taken.
|
194434 | const std::vector<Expr> parameter_expressions = gate.get_params(); | |
123 |
1/2✓ Branch 1 taken 194434 times.
✗ Branch 2 not taken.
|
194434 | const unsigned int number_of_qubits = gate.n_qubits(); | |
124 |
1/2✓ Branch 3 taken 194434 times.
✗ Branch 4 not taken.
|
194434 | std::vector<double> parameters(parameter_expressions.size()); | |
125 |
2/2✓ Branch 1 taken 136482 times.
✓ Branch 2 taken 194434 times.
|
2/2✓ Decision 'true' taken 136482 times.
✓ Decision 'false' taken 194434 times.
|
330916 | for (unsigned nn = 0; nn < parameters.size(); ++nn) { |
126 |
1/2✓ Branch 2 taken 136482 times.
✗ Branch 3 not taken.
|
136482 | const auto optional_value = eval_expr(parameter_expressions[nn]); | |
127 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 136482 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 136482 times.
|
136482 | if (!optional_value) { |
128 | ✗ | std::stringstream ss; | ||
129 | ✗ | ss << get_error_prefix(gate.get_name(), number_of_qubits, parameters) | ||
130 | ✗ | << "parameter[" << nn << "] is symbolic"; | ||
131 | ✗ | throw GateUnitaryMatrixError( | ||
132 | ✗ | ss.str(), GateUnitaryMatrixError::Cause::SYMBOLIC_PARAMETERS); | ||
133 | } | |||
134 |
1/2✓ Branch 1 taken 136482 times.
✗ Branch 2 not taken.
|
136482 | const double value = optional_value.value(); | |
135 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 136482 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 136482 times.
|
136482 | if (!std::isfinite(value)) { |
136 | ✗ | std::stringstream ss; | ||
137 | ✗ | ss << get_error_prefix(gate.get_name(), number_of_qubits, parameters) | ||
138 | ✗ | << "parameter[" << nn << "] has non-finite value " << value; | ||
139 | ✗ | throw GateUnitaryMatrixError( | ||
140 | ✗ | ss.str(), GateUnitaryMatrixError::Cause::NON_FINITE_PARAMETER); | ||
141 | } | |||
142 | 136482 | parameters[nn] = value; | ||
143 | } | |||
144 | 388868 | return parameters; | ||
145 | 194434 | } | ||
146 | ||||
147 | } // namespace internal | |||
148 | } // namespace tket | |||
149 |