6
6
*
7
7
* Licensed under the MIT License
8
8
*/
9
-
10
9
#include " algorithms/StatePreparation.hpp"
11
10
11
+ #include " CircuitOptimizer.hpp"
12
+ #include " ir/operations/StandardOperation.hpp"
13
+
14
+ #include < cmath>
15
+ #include < complex>
16
+ #include < utility>
17
+
12
18
static const double EPS = 1e-10 ;
13
19
14
20
namespace qc {
15
21
using Matrix = std::vector<std::vector<double >>;
16
22
17
- StatePreparation::StatePreparation (
18
- const std::vector<std::complex<double >>& amplitudes) {}
23
+ auto createStatePreparationCircuit (
24
+ const std::vector<std::complex<double >>& amplitudes) -> QuantumComputation {
25
+
26
+ if (!isNormalized (amplitudes)) {
27
+ throw std::invalid_argument{
28
+ " Using State Preparation with Amplitudes that are not normalized" };
29
+ }
30
+
31
+ // get number of qubits needed
32
+ double const numQubits = std::log2 (amplitudes.size ());
33
+
34
+ if (numQubits == 0 || std::floor (numQubits) != numQubits) {
35
+ throw std::invalid_argument{
36
+ " Using State Preparation with vector size that is not a power of 2" };
37
+ }
38
+
39
+ QuantumComputation toZeroCircuit =
40
+ gatesToUncompute (amplitudes, static_cast <size_t >(numQubits));
41
+
42
+ // invert circuit
43
+ CircuitOptimizer::flattenOperations (toZeroCircuit);
44
+ toZeroCircuit.invert ();
45
+
46
+ return toZeroCircuit;
47
+ }
19
48
20
- template <typename T> bool StatePreparation::isNormalized (std::vector<T> vec) {
49
+ template <typename T>
50
+ [[noexcept ]] auto isNormalized (std::vector<T> vec) -> bool {
21
51
return std::abs (1 - twoNorm (vec)) < EPS;
22
52
}
23
53
24
- template <typename T> double StatePreparation:: twoNorm (std::vector<T> vec) {
54
+ template <typename T>[[ noexcept ]] auto twoNorm (std::vector<T> vec) -> double {
25
55
double norm = 0 ;
26
56
for (auto elem : vec) {
27
57
norm += std::norm (elem);
28
58
}
29
59
return sqrt (norm);
30
60
}
31
61
32
- Matrix StatePreparation:: kroneckerProduct (Matrix matrixA, Matrix matrixB) {
62
+ [[ noexcept ]] auto kroneckerProduct (Matrix matrixA, Matrix matrixB) -> Matrix {
33
63
size_t const rowA = matrixA.size ();
34
64
size_t const rowB = matrixB.size ();
35
65
size_t const colA = matrixA[0 ].size ();
@@ -55,7 +85,7 @@ Matrix StatePreparation::kroneckerProduct(Matrix matrixA, Matrix matrixB) {
55
85
return newMatrix;
56
86
}
57
87
58
- Matrix StatePreparation:: createIdentity (size_t size) {
88
+ [[ noexcept ]] auto createIdentity (size_t size) -> Matrix {
59
89
Matrix identity{
60
90
std::vector<std::vector<double >>(size, std::vector<double >(size, 0 ))};
61
91
for (size_t i = 0 ; i < size; ++i) {
@@ -64,9 +94,9 @@ Matrix StatePreparation::createIdentity(size_t size) {
64
94
return identity;
65
95
}
66
96
67
- std::vector< double >
68
- StatePreparation:: matrixVectorProd (const Matrix& matrix,
69
- std::vector<double > vector) {
97
+ [[ noexcept ]] auto
98
+ matrixVectorProd (const Matrix& matrix,
99
+ std::vector<double > vector) -> std::vector< double > {
70
100
std::vector<double > result;
71
101
for (const auto & matrixVec : matrix) {
72
102
double sum{0 };
@@ -79,10 +109,9 @@ StatePreparation::matrixVectorProd(const Matrix& matrix,
79
109
}
80
110
81
111
// creates circuit that takes desired vector to zero
82
- qc::QuantumComputation
83
- StatePreparation::gatesToUncompute (std::vector<std::complex<double >> amplitudes,
84
- size_t numQubits) {
85
- qc::QuantumComputation disentangler{numQubits};
112
+ [[noexcept ]] auto gatesToUncompute (std::vector<std::complex<double >> amplitudes,
113
+ size_t numQubits) -> QuantumComputation {
114
+ QuantumComputation disentangler{numQubits};
86
115
for (size_t i = 0 ; i < numQubits; ++i) {
87
116
// rotations to disentangle LSB
88
117
auto [remainingParams, thetas, phis] = rotationsToDisentangle (amplitudes);
@@ -96,8 +125,8 @@ StatePreparation::gatesToUncompute(std::vector<std::complex<double>> amplitudes,
96
125
}
97
126
if (phisNorm != 0 ) {
98
127
// call multiplex with RZGate
99
- qc:: QuantumComputation rzMultiplexer =
100
- multiplex (qc:: OpType{qc:: RZ}, phis, addLastCnot);
128
+ QuantumComputation rzMultiplexer =
129
+ multiplex (OpType{RZ}, phis, addLastCnot);
101
130
// append rzMultiplexer to disentangler, but it should only attach on
102
131
// qubits i-numQubits, thus "i" is added to the local qubit indices
103
132
for (auto & op : rzMultiplexer) {
@@ -107,16 +136,16 @@ StatePreparation::gatesToUncompute(std::vector<std::complex<double>> amplitudes,
107
136
for (auto control : op->getControls ()) {
108
137
// there were some errors when accessing the qubit directly and
109
138
// adding to it
110
- op->setControls (qc::Controls{
111
- qc:: Control{control.qubit + static_cast <unsigned int >(i)}});
139
+ op->setControls (
140
+ Controls{ Control{control.qubit + static_cast <unsigned int >(i)}});
112
141
}
113
142
}
114
- disentangler.emplace_back <qc:: Operation>(rzMultiplexer.asOperation ());
143
+ disentangler.emplace_back <Operation>(rzMultiplexer.asOperation ());
115
144
}
116
145
if (thetasNorm != 0 ) {
117
146
// call multiplex with RYGate
118
- qc:: QuantumComputation ryMultiplexer =
119
- multiplex (qc:: OpType{qc:: RY}, thetas, addLastCnot);
147
+ QuantumComputation ryMultiplexer =
148
+ multiplex (OpType{RY}, thetas, addLastCnot);
120
149
// append reversed ry_multiplexer to disentangler, but it should only
121
150
// attach on qubits i-numQubits, thus "i" is added to the local qubit
122
151
// indices
@@ -128,11 +157,11 @@ StatePreparation::gatesToUncompute(std::vector<std::complex<double>> amplitudes,
128
157
for (auto control : op->getControls ()) {
129
158
// there were some errors when accessing the qubit directly and
130
159
// adding to it
131
- op->setControls (qc::Controls{
132
- qc:: Control{control.qubit + static_cast <unsigned int >(i)}});
160
+ op->setControls (
161
+ Controls{ Control{control.qubit + static_cast <unsigned int >(i)}});
133
162
}
134
163
}
135
- disentangler.emplace_back <qc:: Operation>(ryMultiplexer.asOperation ());
164
+ disentangler.emplace_back <Operation>(ryMultiplexer.asOperation ());
136
165
}
137
166
}
138
167
// adjust global phase according to the last e^(it)
@@ -146,10 +175,10 @@ StatePreparation::gatesToUncompute(std::vector<std::complex<double>> amplitudes,
146
175
147
176
// works out Ry and Rz rotation angles used to disentangle LSB qubit
148
177
// rotations make up block diagonal matrix U
149
- std::tuple<std::vector<std::complex< double >>, std::vector< double >,
150
- std::vector<double >>
151
- StatePreparation::rotationsToDisentangle (
152
- std::vector<std::complex< double >> amplitudes) {
178
+ [[ noexcept ]] auto
179
+ rotationsToDisentangle ( std::vector<std::complex< double >> amplitudes)
180
+ -> std::tuple<std::vector<std::complex< double >>, std::vector< double >,
181
+ std::vector<double >> {
153
182
std::vector<std::complex<double >> remainingVector;
154
183
std::vector<double > thetas;
155
184
std::vector<double > phis;
@@ -164,9 +193,9 @@ StatePreparation::rotationsToDisentangle(
164
193
return {remainingVector, thetas, phis};
165
194
}
166
195
167
- std::tuple<std:: complex<double >, double , double >
168
- StatePreparation::blochAngles ( std::complex<double > const complexA,
169
- std::complex<double > const complexB) {
196
+ [[ noexcept ]] auto blochAngles ( std::complex<double > const complexA,
197
+ std::complex<double > const complexB)
198
+ -> std::tuple<std:: complex<double>, double, double> {
170
199
double theta{0 };
171
200
double phi{0 };
172
201
double finalT{0 };
@@ -191,18 +220,17 @@ StatePreparation::blochAngles(std::complex<double> const complexA,
191
220
* @param lastCnot : add last cnot if true
192
221
* @return multiplexer circuit as QuantumComputation
193
222
*/
194
- qc::QuantumComputation StatePreparation::multiplex (qc::OpType targetGate,
195
- std::vector<double > angles,
196
- bool lastCnot) {
223
+ [[noexcept ]] auto multiplex (OpType targetGate, std::vector<double > angles,
224
+ bool lastCnot) -> QuantumComputation {
197
225
size_t const listLen = angles.size ();
198
226
double const localNumQubits =
199
227
std::floor (std::log2 (static_cast <double >(listLen))) + 1 ;
200
- qc:: QuantumComputation multiplexer{static_cast <size_t >(localNumQubits)};
228
+ QuantumComputation multiplexer{static_cast <size_t >(localNumQubits)};
201
229
// recursion base case
202
230
if (localNumQubits == 1 ) {
203
- multiplexer.emplace_back <qc:: StandardOperation>(
204
- multiplexer. getNqubits (), qc:: Controls{}, 0 , targetGate,
205
- std::vector{angles[0 ]});
231
+ multiplexer.emplace_back <StandardOperation>(multiplexer. getNqubits (),
232
+ Controls{}, 0 , targetGate,
233
+ std::vector{angles[0 ]});
206
234
return multiplexer;
207
235
}
208
236
@@ -218,31 +246,31 @@ qc::QuantumComputation StatePreparation::multiplex(qc::OpType targetGate,
218
246
std::make_move_iterator (angles.begin ()),
219
247
std::make_move_iterator (angles.begin () +
220
248
static_cast <int64_t >(listLen) / 2 )};
221
- qc:: QuantumComputation multiplex1 = multiplex (targetGate, angles1, false );
249
+ QuantumComputation multiplex1 = multiplex (targetGate, angles1, false );
222
250
223
251
// append multiplex1 to multiplexer
224
- multiplexer.emplace_back <qc:: Operation>(multiplex1.asOperation ());
252
+ multiplexer.emplace_back <Operation>(multiplex1.asOperation ());
225
253
// flips the LSB qubit, control on MSB
226
- multiplexer.cx (0 , static_cast <qc:: Qubit>(localNumQubits - 1 ));
254
+ multiplexer.cx (0 , static_cast <Qubit>(localNumQubits - 1 ));
227
255
228
256
std::vector<double > const angles2{std::make_move_iterator (angles.begin ()) +
229
257
static_cast <int64_t >(listLen) / 2 ,
230
258
std::make_move_iterator (angles.end ())};
231
- qc:: QuantumComputation multiplex2 = multiplex (targetGate, angles2, false );
259
+ QuantumComputation multiplex2 = multiplex (targetGate, angles2, false );
232
260
233
261
// extra efficiency by reversing (!= inverting) second multiplex
234
262
if (listLen > 1 ) {
235
263
multiplex2.reverse ();
236
- multiplexer.emplace_back <qc:: Operation>(multiplex2.asOperation ());
264
+ multiplexer.emplace_back <Operation>(multiplex2.asOperation ());
237
265
} else {
238
- multiplexer.emplace_back <qc:: Operation>(multiplex2.asOperation ());
266
+ multiplexer.emplace_back <Operation>(multiplex2.asOperation ());
239
267
}
240
268
241
269
if (lastCnot) {
242
- multiplexer.cx (0 , static_cast <qc:: Qubit>(localNumQubits - 1 ));
270
+ multiplexer.cx (0 , static_cast <Qubit>(localNumQubits - 1 ));
243
271
}
244
272
245
- qc:: CircuitOptimizer::flattenOperations (multiplexer);
273
+ CircuitOptimizer::flattenOperations (multiplexer);
246
274
return multiplexer;
247
275
}
248
276
0 commit comments