1 /**
2 * This code is part of Qiskit.
3 *
4 * (C) Copyright IBM 2018, 2019.
5 *
6 * This code is licensed under the Apache License, Version 2.0. You may
7 * obtain a copy of this license in the LICENSE.txt file in the root directory
8 * of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
9 *
10 * Any modifications or derivative works of this code must retain this
11 * copyright notice, and modified files need to carry a notice indicating
12 * that they have been altered from the originals.
13 */
14
15 #ifndef _aer_framework_utils_hpp_
16 #define _aer_framework_utils_hpp_
17
18 #include <algorithm>
19 #include <sstream>
20 #include <cmath>
21 #include <limits>
22
23 #include "framework/types.hpp"
24
25 namespace AER {
26 namespace Utils {
27
28 //------------------------------------------------------------------------------
29 // Static Matrices
30 //------------------------------------------------------------------------------
31
32 class Matrix {
33 public:
34 // Single-qubit gates
35 const static cmatrix_t I; // name: "id"
36 const static cmatrix_t X; // name: "x"
37 const static cmatrix_t Y; // name: "y"
38 const static cmatrix_t Z; // name: "z"
39 const static cmatrix_t H; // name: "h"
40 const static cmatrix_t S; // name: "s"
41 const static cmatrix_t SDG; // name: "sdg"
42 const static cmatrix_t T; // name: "t"
43 const static cmatrix_t TDG; // name: "tdg"
44 const static cmatrix_t X90; // name: "x90"
45
46 // Two-qubit gates
47 const static cmatrix_t CX; // name: "cx"
48 const static cmatrix_t CZ; // name: "cz"
49 const static cmatrix_t SWAP; // name: "swap"
50 const static cmatrix_t CR; // TODO
51 const static cmatrix_t CR90; // TODO
52
53 // Identity Matrix
54 static cmatrix_t identity(size_t dim);
55
56 // Single-qubit waltz gates
57 static cmatrix_t u1(double lam);
58 static cmatrix_t u2(double phi, double lam);
59 static cmatrix_t u3(double theta, double phi, double lam);
60
61 // Complex arguments are implemented by taking std::real
62 // of the input
u1(complex_t lam)63 static cmatrix_t u1(complex_t lam) {return u1(std::real(lam));}
u2(complex_t phi,complex_t lam)64 static cmatrix_t u2(complex_t phi, complex_t lam) {
65 return u2(std::real(phi), std::real(lam));
66 }
u3(complex_t theta,complex_t phi,complex_t lam)67 static cmatrix_t u3(complex_t theta, complex_t phi, complex_t lam) {
68 return u3(std::real(theta), std::real(phi), std::real(lam));
69 };
70
71 // Return the matrix for a named matrix string
72 // Allowed names correspond to all the const static single-qubit
73 // and two-qubit gate members
from_name(const std::string & name)74 static const cmatrix_t from_name(const std::string &name) {
75 return *label_map_.at(name);
76 }
77
78 // Check if the input name string is allowed
allowed_name(const std::string & name)79 static bool allowed_name(const std::string &name) {
80 return (label_map_.find(name) != label_map_.end());
81 }
82
83 private:
84 // Lookup table that returns a pointer to the static data member
85 const static stringmap_t<const cmatrix_t*> label_map_;
86 };
87
88 //------------------------------------------------------------------------------
89 // Static Vectorized Matrices
90 //------------------------------------------------------------------------------
91 class VMatrix {
92 public:
93 // Single-qubit gates
94 const static cvector_t I; // name: "id"
95 const static cvector_t X; // name: "x"
96 const static cvector_t Y; // name: "y"
97 const static cvector_t Z; // name: "z"
98 const static cvector_t H; // name: "h"
99 const static cvector_t S; // name: "s"
100 const static cvector_t SDG; // name: "sdg"
101 const static cvector_t T; // name: "t"
102 const static cvector_t TDG; // name: "tdg"
103 const static cvector_t X90; // name: "x90"
104
105 // Two-qubit gates
106 const static cvector_t CX; // name: "cx"
107 const static cvector_t CZ; // name: "cz"
108 const static cvector_t SWAP; // name: "swap"
109 const static cvector_t CR; // TODO
110 const static cvector_t CR90; // TODO
111
112 // Identity Matrix
113 static cvector_t identity(size_t dim);
114
115 // Single-qubit waltz gates
116 static cvector_t u1(double lam);
117 static cvector_t u2(double phi, double lam);
118 static cvector_t u3(double theta, double phi, double lam);
119
120 // Complex arguments are implemented by taking std::real
121 // of the input
u1(complex_t lam)122 static cvector_t u1(complex_t lam) {return u1(std::real(lam));}
u2(complex_t phi,complex_t lam)123 static cvector_t u2(complex_t phi, complex_t lam) {
124 return u2(std::real(phi), std::real(lam));
125 }
u3(complex_t theta,complex_t phi,complex_t lam)126 static cvector_t u3(complex_t theta, complex_t phi, complex_t lam) {
127 return u3(std::real(theta), std::real(phi), std::real(lam));
128 };
129
130 // Return the matrix for a named matrix string
131 // Allowed names correspond to all the const static single-qubit
132 // and two-qubit gate members
from_name(const std::string & name)133 static const cvector_t from_name(const std::string &name) {
134 return *label_map_.at(name);
135 }
136
137 // Check if the input name string is allowed
allowed_name(const std::string & name)138 static bool allowed_name(const std::string &name) {
139 return (label_map_.find(name) != label_map_.end());
140 }
141
142
143 private:
144 // Lookup table that returns a pointer to the static data member
145 const static stringmap_t<const cvector_t*> label_map_;
146 };
147
148
149 //------------------------------------------------------------------------------
150 // Static Superoperator Matrices
151 //------------------------------------------------------------------------------
152
153 class SMatrix {
154 public:
155 // Single-qubit gates
156 const static cmatrix_t I; // name: "id"
157 const static cmatrix_t X; // name: "x"
158 const static cmatrix_t Y; // name: "y"
159 const static cmatrix_t Z; // name: "z"
160 const static cmatrix_t H; // name: "h"
161 const static cmatrix_t S; // name: "s"
162 const static cmatrix_t SDG; // name: "sdg"
163 const static cmatrix_t T; // name: "t"
164 const static cmatrix_t TDG; // name: "tdg"
165 const static cmatrix_t X90; // name: "x90"
166
167 // Two-qubit gates
168 const static cmatrix_t CX; // name: "cx"
169 const static cmatrix_t CZ; // name: "cz"
170 const static cmatrix_t SWAP; // name: "swap"
171
172 // Identity Matrix
173 static cmatrix_t identity(size_t dim);
174
175 // Single-qubit waltz gates
176 static cmatrix_t u1(double lam);
177 static cmatrix_t u2(double phi, double lam);
178 static cmatrix_t u3(double theta, double phi, double lam);
179
180 // Complex arguments are implemented by taking std::real
181 // of the input
u1(complex_t lam)182 static cmatrix_t u1(complex_t lam) {return u1(std::real(lam));}
u2(complex_t phi,complex_t lam)183 static cmatrix_t u2(complex_t phi, complex_t lam) {
184 return u2(std::real(phi), std::real(lam));
185 }
u3(complex_t theta,complex_t phi,complex_t lam)186 static cmatrix_t u3(complex_t theta, complex_t phi, complex_t lam) {
187 return u3(std::real(theta), std::real(phi), std::real(lam));
188 };
189
190 // Return superoperator matrix for reset instruction
191 // on specified dim statespace.
192 // The returned matrix is (dim * dim, dim * dim).
193 static cmatrix_t reset(size_t dim);
194
195 // Return the matrix for a named matrix string
196 // Allowed names correspond to all the const static single-qubit
197 // and two-qubit gate members
from_name(const std::string & name)198 static const cmatrix_t from_name(const std::string &name) {
199 return *label_map_.at(name);
200 }
201
202 // Check if the input name string is allowed
allowed_name(const std::string & name)203 static bool allowed_name(const std::string &name) {
204 return (label_map_.find(name) != label_map_.end());
205 }
206
207 private:
208 // Lookup table that returns a pointer to the static data member
209 const static stringmap_t<const cmatrix_t*> label_map_;
210 };
211
212 //------------------------------------------------------------------------------
213 // Matrix Functions
214 //------------------------------------------------------------------------------
215
216 // Construct a matrix from a vector of matrix-row vectors
217 template<class T> matrix<T> make_matrix(const std::vector<std::vector<T>> &mat);
218
219 // Reshape a length column-major vectorized matrix into a square matrix
220 template<class T> matrix<T> devectorize_matrix(const std::vector<T> &vec);
221
222 // Vectorize a matrix by stacking matrix columns (column-major vectorization)
223 template<class T> std::vector<T> vectorize_matrix(const matrix<T> &mat);
224
225 // Return the transpose a matrix
226 template <class T> matrix<T> transpose(const matrix<T> &A);
227
228 // Return the adjoing (Hermitian-conjugate) of a matrix
229 template <class T>
230 matrix<std::complex<T>> dagger(const matrix<std::complex<T>> &A);
231
232 // Return the complex conjugate of a matrix
233 template <class T>
234 matrix<std::complex<T>> conjugate(const matrix<std::complex<T>> &A);
235
236 // Given a list of matrices for a multiplexer stacks and packs them 0/1/2/...
237 // into a single 2^control x (2^target x 2^target) cmatrix_t)
238 // Equivalent to a 2^qubits x 2^target "flat" matrix
239 template<class T>
240 matrix<T> stacked_matrix(const std::vector<matrix<T>> &mmat);
241
242 // Return a vector containing the diagonal of a matrix
243 template<class T> std::vector<T> matrix_diagonal(const matrix<T>& mat);
244
245 // Inplace transformations
246 template <class T> matrix<T>& transpose_inplace(matrix<T> &A);
247 template <class T>
248 matrix<std::complex<T>>& dagger_inplace(matrix<std::complex<T>> &A);
249 template <class T>
250 matrix<std::complex<T>>& conjugate_inplace(matrix<std::complex<T>> &A);
251
252 // Tracing
253 template <class T> T trace(const matrix<T> &A);
254 template <class T> matrix<T> partial_trace_a(const matrix<T> &rho, size_t dimA);
255 template <class T> matrix<T> partial_trace_b(const matrix<T> &rho, size_t dimB);
256
257 // Tensor product
258 template <class T> matrix<T> tensor_product(const matrix<T> &A, const matrix<T> &B);
259 template <class T> matrix<T> unitary_superop(const matrix<T> &mat);
260
261 // concatenate
262 // Returns a matrix that is the concatenation of two matrices A, B
263 // The matrices must have the same dimensions
264 // If axis == 0, place rows of B after rows of A (vertical extension)
265 // If axis == 1, place columns of B after columns of A (horizontal extension)
266 template <class T> matrix<T> concatenate (const matrix<T> &A, const matrix<T> &B, uint_t axis);
267
268 // split
269 // Splits A into 2 matrices B and C equal in dimensions
270 // If axis == 0, split A by rows. A must have an even number of rows.
271 // If axis == 1, split A by columns. A must have an even number of columns.
272 template <class T> void split (const matrix<T> &A, matrix<T> &B, matrix<T> &C, uint_t axis);
273
274 //Elementwise matrix multiplication
275 template <class T> matrix<T> elementwise_multiplication(const matrix<T> &A, const matrix<T> &B);
276
277 //Matrix sum of elements
278 template <class T> T sum(const matrix<T> &A);
279
280 // Matrix comparison
281 template <class T>
282 bool is_square(const matrix<T> &mat);
283
284 template <class T>
285 bool is_diagonal(const matrix<T> &mat);
286
287 template <class T>
288 bool is_equal(const matrix<T> &mat1, const matrix<T> &mat2, double threshold);
289
290 template <class T>
291 bool is_diagonal(const matrix<T> &mat, double threshold);
292
293 template <class T>
294 std::pair<bool, double> is_identity_phase(const matrix<T> &mat, double threshold);
295
296 template <class T>
297 bool is_identity(const matrix<T> &mat, double threshold);
298
299 template <class T>
300 bool is_diagonal_identity(const matrix<T> &mat, double threshold);
301
302 template <class T>
303 bool is_unitary(const matrix<T> &mat, double threshold);
304
305 template <class T>
306 bool is_hermitian(const matrix<T> &mat, double threshold);
307
308 template <class T>
309 bool is_symmetrix(const matrix<T> &mat, double threshold);
310
311 template <class T>
312 bool is_cptp_kraus(const std::vector<matrix<T>> &kraus, double threshold);
313
314 //------------------------------------------------------------------------------
315 // Vector functions
316 //------------------------------------------------------------------------------
317
318 // Return true of the vector has norm-1.
319 template <typename T>
320 double is_unit_vector(const std::vector<T> &vec);
321
322 // Conjugate a vector
323 template <typename T>
324 std::vector<std::complex<T>> conjugate(const std::vector<std::complex<T>> &v);
325
326 // Compute the Euclidean 2-norm of a vector
327 template <typename T>
328 double norm(const std::vector<T> &vec);
329
330 // Return the matrix formed by taking the outproduct of two vector |ket><bra|
331 template <typename T>
332 matrix<T> outer_product(const std::vector<T> &ket, const std::vector<T> &bra);
333
334 template <typename T>
projector(const std::vector<T> & ket)335 inline matrix<T> projector(const std::vector<T> &ket) {return outer_product(ket, ket);}
336
337 // Tensor product vector
338 template <typename T>
339 std::vector<T> tensor_product(const std::vector<T> &v, const std::vector<T> &w);
340
341 // Return a new vector formed by multiplying each element of the input vector
342 // with a scalar. The product of types T1 * T2 must be valid.
343 template <typename T1, typename T2>
344 std::vector<T1> scalar_multiply(const std::vector<T1> &vec, T2 val);
345
346 // Inplace multiply each entry in a vector by a scalar and returns a reference to
347 // the input vector argument. The product of types T1 * T2 must be valid.
348 template <typename T1, typename T2>
349 std::vector<T1>& scalar_multiply_inplace(std::vector<T1> &vec, T2 scalar);
350
351 // Truncate the first argument its absolute value is less than epsilon
352 // this function returns a refernce to the chopped first argument
353 double &chop_inplace(double &val, double epsilon);
354 std::complex<double> &chop_inplace(std::complex<double> &val, double epsilon);
355
356 double chop(double val, double epsilon);
357
358 // As above for complex first arguments
359 template <typename T>
360 std::complex<T> chop(std::complex<T> val, double epsilon);
361 // Truncate each element in a vector if its absolute value is less than epsilon
362 // This function returns a reference to the chopped input vector
363 template <typename T>
364 std::vector<T> &chop_inplace(std::vector<T> &vec, double epsilon);
365
366 template <typename T>
367 std::vector<T> chop(const std::vector<T> &vec, double epsilon);
368
369 // Add rhs vector to lhs using move semantics.
370 // rhs should not be used after this operation.
371 template <class T>
372 void combine(std::vector<T> &lhs, const std::vector<T> &rhs);
373
374
375 // Convert a dense vector into sparse ket form.
376 // epsilon determins the threshold for which small values will be removed from
377 // the output. The base of the ket (2-10 for qudits, or 16 for hexadecimal)
378 // specifies the subsystem dimension and the base of the dit-string labels.
379 template <typename T>
380 std::map<std::string, T> vec2ket(const std::vector<T> &vec, double epsilon, uint_t base = 2);
381
382 //------------------------------------------------------------------------------
383 // Bit Conversions
384 //------------------------------------------------------------------------------
385
386 // Format a hex string so that it has a prefix "0x", abcdef chars are lowercase
387 // and leading zeros are removed
388 // Example: 0010A -> 0x10a
389 std::string format_hex(const std::string &hex);
390 std::string& format_hex_inplace(std::string &hex);
391
392 // Pad string with a char if it is less
393 std::string padleft(const std::string &s, char c, size_t min_length);
394 std::string& padleft_inplace(std::string &s, char c, size_t min_length);
395
396 // Convert integers and hexadecimals to register vectors
397 reg_t int2reg(uint_t n, uint_t base = 2);
398 reg_t int2reg(uint_t n, uint_t base, uint_t minlen);
399 reg_t hex2reg(std::string str);
400
401 // Convert bit-strings to hex-strings
402 // if prefix is true "0x" will prepend the output string
403 std::string bin2hex(const std::string bin, bool prefix = true);
404
405 // Convert hex-strings to bit-strings
406 // if prefix is true "0b" will prepend the output string
407 std::string hex2bin(const std::string bs, bool prefix = true);
408
409 // Convert 64-bit unsigned integers to dit-string (dit base = 2 to 10)
410 std::string int2string(uint_t n, uint_t base = 2);
411 std::string int2string(uint_t n, uint_t base, uint_t length);
412
413 // Convert integers to bit-strings
int2bin(uint_t n)414 inline std::string int2bin(uint_t n) {return int2string(n, 2);}
int2bin(uint_t n,uint_t length)415 inline std::string int2bin(uint_t n, uint_t length) {return int2string(n, 2, length);}
416
417 // Convert integers to hex-strings
int2hex(uint_t n)418 inline std::string int2hex(uint_t n) {return bin2hex(int2bin(n));}
419
420 // Convert reg to int
421 uint_t reg2int(const reg_t ®, uint_t base);
422
423 //==============================================================================
424 // Implementations: Static Matrices
425 //==============================================================================
426
427 const cmatrix_t Matrix::I = make_matrix<complex_t>({{{1, 0}, {0, 0}},
428 {{0, 0}, {1, 0}}});
429
430 const cmatrix_t Matrix::X = make_matrix<complex_t>({{{0, 0}, {1, 0}},
431 {{1, 0}, {0, 0}}});
432
433 const cmatrix_t Matrix::Y = make_matrix<complex_t>({{{0, 0}, {0, -1}},
434 {{0, 1}, {0, 0}}});
435
436 const cmatrix_t Matrix::Z = make_matrix<complex_t>({{{1, 0}, {0, 0}},
437 {{0, 0}, {-1, 0}}});
438
439 const cmatrix_t Matrix::S = make_matrix<complex_t>({{{1, 0}, {0, 0}},
440 {{0, 0}, {0, 1}}});
441
442 const cmatrix_t Matrix::SDG = make_matrix<complex_t>({{{1, 0}, {0, 0}},
443 {{0, 0}, {0, -1}}});
444 const cmatrix_t Matrix::T = make_matrix<complex_t>({{{1, 0}, {0, 0}},
445 {{0, 0}, {1 / std::sqrt(2), 1 / std::sqrt(2)}}});
446
447 const cmatrix_t Matrix::TDG = make_matrix<complex_t>({{{1, 0}, {0, 0}},
448 {{0, 0}, {1 / std::sqrt(2), -1 / std::sqrt(2)}}});
449
450 const cmatrix_t Matrix::H = make_matrix<complex_t>({{{1 / std::sqrt(2.), 0}, {1 / std::sqrt(2.), 0}},
451 {{1 / std::sqrt(2.), 0}, {-1 / std::sqrt(2.), 0}}});
452
453 const cmatrix_t Matrix::X90 = make_matrix<complex_t>({{{1. / std::sqrt(2.), 0}, {0, -1. / std::sqrt(2.)}},
454 {{0, -1. / std::sqrt(2.)}, {1. / std::sqrt(2.), 0}}});
455
456 const cmatrix_t Matrix::CX = make_matrix<complex_t>({{{1, 0}, {0, 0}, {0, 0}, {0, 0}},
457 {{0, 0}, {0, 0}, {0, 0}, {1, 0}},
458 {{0, 0}, {0, 0}, {1, 0}, {0, 0}},
459 {{0, 0}, {1, 0}, {0, 0}, {0, 0}}});
460
461 const cmatrix_t Matrix::CZ = make_matrix<complex_t>({{{1, 0}, {0, 0}, {0, 0}, {0, 0}},
462 {{0, 0}, {1, 0}, {0, 0}, {0, 0}},
463 {{0, 0}, {0, 0}, {1, 0}, {0, 0}},
464 {{0, 0}, {0, 0}, {0, 0}, {-1, 0}}});
465
466 const cmatrix_t Matrix::SWAP = make_matrix<complex_t>({{{1, 0}, {0, 0}, {0, 0}, {0, 0}},
467 {{0, 0}, {0, 0}, {1, 0}, {0, 0}},
468 {{0, 0}, {1, 0}, {0, 0}, {0, 0}},
469 {{0, 0}, {0, 0}, {0, 0}, {1, 0}}});
470
471 // TODO const cmatrix_t Matrix::CR = ...
472 // TODO const cmatrix_t Matrix::CR90 = ...
473
474 // Lookup table
475 const stringmap_t<const cmatrix_t*> Matrix::label_map_ = {
476 {"id", &Matrix::I}, {"x", &Matrix::X}, {"y", &Matrix::Y}, {"z", &Matrix::Z},
477 {"h", &Matrix::H}, {"s", &Matrix::S}, {"sdg", &Matrix::SDG},
478 {"t", &Matrix::T}, {"tdg", &Matrix::TDG}, {"x90", &Matrix::X90},
479 {"cx", &Matrix::CX}, {"cz", &Matrix::CZ}, {"swap", &Matrix::SWAP}
480 };
481
identity(size_t dim)482 cmatrix_t Matrix::identity(size_t dim) {
483 cmatrix_t mat(dim, dim);
484 for (size_t j=0; j<dim; j++)
485 mat(j, j) = {1.0, 0.0};
486 return mat;
487 }
488
489
u1(double lambda)490 cmatrix_t Matrix::u1(double lambda) {
491 cmatrix_t mat(2, 2);
492 mat(0, 0) = {1., 0.};
493 mat(1, 1) = std::exp(complex_t(0., lambda));
494 return mat;
495 }
496
497
u2(double phi,double lambda)498 cmatrix_t Matrix::u2(double phi, double lambda) {
499 cmatrix_t mat(2, 2);
500 const complex_t i(0., 1.);
501 const complex_t invsqrt2(1. / std::sqrt(2), 0.);
502 mat(0, 0) = invsqrt2;
503 mat(0, 1) = -std::exp(i * lambda) * invsqrt2;
504 mat(1, 0) = std::exp(i * phi) * invsqrt2;
505 mat(1, 1) = std::exp(i * (phi + lambda)) * invsqrt2;
506 return mat;
507 }
508
509
u3(double theta,double phi,double lambda)510 cmatrix_t Matrix::u3(double theta, double phi, double lambda) {
511 cmatrix_t mat(2, 2);
512 const complex_t i(0., 1.);
513 mat(0, 0) = std::cos(theta / 2.);
514 mat(0, 1) = -std::exp(i * lambda) * std::sin(theta / 2.);
515 mat(1, 0) = std::exp(i * phi) * std::sin(theta / 2.);
516 mat(1, 1) = std::exp(i * (phi + lambda)) * std::cos(theta / 2.);
517 return mat;
518 }
519
520
521 //==============================================================================
522 // Implementations: Static Matrices
523 //==============================================================================
524
525 const cvector_t VMatrix::I = vectorize_matrix(Matrix::I);
526
527 const cvector_t VMatrix::X = vectorize_matrix(Matrix::X);
528
529 const cvector_t VMatrix::Y = vectorize_matrix(Matrix::Y);
530
531 const cvector_t VMatrix::Z = vectorize_matrix(Matrix::Z);
532
533 const cvector_t VMatrix::S = vectorize_matrix(Matrix::S);
534
535 const cvector_t VMatrix::SDG = vectorize_matrix(Matrix::SDG);
536
537 const cvector_t VMatrix::T = vectorize_matrix(Matrix::T);
538
539 const cvector_t VMatrix::TDG = vectorize_matrix(Matrix::TDG);
540
541 const cvector_t VMatrix::H = vectorize_matrix(Matrix::H);
542
543 const cvector_t VMatrix::X90 = vectorize_matrix(Matrix::X90);
544
545 const cvector_t VMatrix::CX = vectorize_matrix(Matrix::CX);
546
547 const cvector_t VMatrix::CZ = vectorize_matrix(Matrix::CZ);
548
549 const cvector_t VMatrix::SWAP = vectorize_matrix(Matrix::SWAP);
550
551 // TODO const cvector_t VMatrix::CR = ...
552 // TODO const cvector_t VMatrix::CR90 = ...
553
554 // Lookup table
555 const stringmap_t<const cvector_t*> VMatrix::label_map_ = {
556 {"id", &VMatrix::I}, {"x", &VMatrix::X}, {"y", &VMatrix::Y}, {"z", &VMatrix::Z},
557 {"h", &VMatrix::H}, {"s", &VMatrix::S}, {"sdg", &VMatrix::SDG},
558 {"t", &VMatrix::T}, {"tdg", &VMatrix::TDG}, {"x90", &VMatrix::X90},
559 {"cx", &VMatrix::CX}, {"cz", &VMatrix::CZ}, {"swap", &VMatrix::SWAP}
560 };
561
identity(size_t dim)562 cvector_t VMatrix::identity(size_t dim) {
563 cvector_t mat(dim * dim);
564 for (size_t j=0; j<dim; j++)
565 mat[j + j * dim] = {1.0, 0.0};
566 return mat;
567 }
568
569
u1(double lambda)570 cvector_t VMatrix::u1(double lambda) {
571 cvector_t mat(2 * 2);
572 mat[0 + 0 * 2] = {1., 0.};
573 mat[1 + 1 * 2] = std::exp(complex_t(0., lambda));
574 return mat;
575 }
576
577
u2(double phi,double lambda)578 cvector_t VMatrix::u2(double phi, double lambda) {
579 cvector_t mat(2 * 2);
580 const complex_t i(0., 1.);
581 const complex_t invsqrt2(1. / std::sqrt(2), 0.);
582 mat[0 + 0 * 2] = invsqrt2;
583 mat[0 + 1 * 2] = -std::exp(i * lambda) * invsqrt2;
584 mat[1 + 0 * 2] = std::exp(i * phi) * invsqrt2;
585 mat[1 + 1 * 2] = std::exp(i * (phi + lambda)) * invsqrt2;
586 return mat;
587 }
588
u3(double theta,double phi,double lambda)589 cvector_t VMatrix::u3(double theta, double phi, double lambda) {
590 cvector_t mat(2 * 2);
591 const complex_t i(0., 1.);
592 mat[0 + 0 * 2] = std::cos(theta / 2.);
593 mat[0 + 1 * 2] = -std::exp(i * lambda) * std::sin(theta / 2.);
594 mat[1 + 0 * 2] = std::exp(i * phi) * std::sin(theta / 2.);
595 mat[1 + 1 * 2] = std::exp(i * (phi + lambda)) * std::cos(theta / 2.);
596 return mat;
597 }
598
599 //==============================================================================
600 // Implementations: Static Matrices
601 //==============================================================================
602
603 const cmatrix_t SMatrix::I = unitary_superop(Matrix::I);
604
605 const cmatrix_t SMatrix::X = unitary_superop(Matrix::X);
606
607 const cmatrix_t SMatrix::Y = unitary_superop(Matrix::Y);
608
609 const cmatrix_t SMatrix::Z = unitary_superop(Matrix::Z);
610
611 const cmatrix_t SMatrix::S = unitary_superop(Matrix::S);
612
613 const cmatrix_t SMatrix::SDG = unitary_superop(Matrix::SDG);
614
615 const cmatrix_t SMatrix::T = unitary_superop(Matrix::T);
616
617 const cmatrix_t SMatrix::TDG = unitary_superop(Matrix::TDG);
618
619 const cmatrix_t SMatrix::H = unitary_superop(Matrix::H);
620
621 const cmatrix_t SMatrix::X90 = unitary_superop(Matrix::X90);
622
623 const cmatrix_t SMatrix::CX = unitary_superop(Matrix::CX);
624
625 const cmatrix_t SMatrix::CZ = unitary_superop(Matrix::CZ);
626
627 const cmatrix_t SMatrix::SWAP = unitary_superop(Matrix::SWAP);
628
629 // Lookup table
630 const stringmap_t<const cmatrix_t*> SMatrix::label_map_ = {
631 {"id", &SMatrix::I}, {"x", &SMatrix::X}, {"y", &SMatrix::Y}, {"z", &SMatrix::Z},
632 {"h", &SMatrix::H}, {"s", &SMatrix::S}, {"sdg", &SMatrix::SDG},
633 {"t", &SMatrix::T}, {"tdg", &SMatrix::TDG}, {"x90", &SMatrix::X90},
634 {"cx", &SMatrix::CX}, {"cz", &SMatrix::CZ}, {"swap", &SMatrix::SWAP}
635 };
636
identity(size_t dim)637 cmatrix_t SMatrix::identity(size_t dim) {
638 return Matrix::identity(dim * dim);
639 }
640
641
u1(double lambda)642 cmatrix_t SMatrix::u1(double lambda) {
643 cmatrix_t mat(4, 4);
644 mat(0, 0) = {1., 0.};
645 mat(1, 1) = std::exp(complex_t(0., lambda));
646 mat(2, 2) = std::exp(complex_t(0., -lambda));
647 mat(3, 3) = {1., 0.};
648 return mat;
649 }
650
651
u2(double phi,double lambda)652 cmatrix_t SMatrix::u2(double phi, double lambda) {
653 return tensor_product(Matrix::u2(-phi, -lambda),
654 Matrix::u2(phi, lambda));
655 }
656
657
u3(double theta,double phi,double lambda)658 cmatrix_t SMatrix::u3(double theta, double phi, double lambda) {
659 return tensor_product(Matrix::u3(theta, -phi, -lambda),
660 Matrix::u3(theta, phi, lambda));
661 }
662
663
reset(size_t dim)664 cmatrix_t SMatrix::reset(size_t dim) {
665 cmatrix_t mat(dim * dim, dim * dim);
666 for (size_t j=0; j < dim; j++) {
667 mat(0, j * (dim + 1)) = 1.;
668 }
669 return mat;
670 }
671
672
673 //==============================================================================
674 // Implementations: Matrix functions
675 //==============================================================================
676
677 template<class T>
devectorize_matrix(const std::vector<T> & vec)678 matrix<T> devectorize_matrix(const std::vector<T>& vec) {
679 size_t dim = std::sqrt(vec.size());
680 matrix<T> mat(dim, dim);
681 for (size_t col=0; col < dim; col++)
682 for (size_t row=0; row < dim; row++) {
683 mat(row, col) = vec[dim * col + row];
684 }
685 return mat;
686 }
687
688 template<class T>
vectorize_matrix(const matrix<T> & mat)689 std::vector<T> vectorize_matrix(const matrix<T>& mat) {
690 std::vector<T> vec;
691 vec.resize(mat.size(), 0.);
692 size_t nrows = mat.GetRows();
693 size_t ncols = mat.GetColumns();
694 for (size_t col=0; col < ncols; col++)
695 for (size_t row=0; row < nrows; row++) {
696 vec[nrows * col + row] = mat(row, col);
697 }
698 return vec;
699 }
700
701 template <class T>
make_matrix(const std::vector<std::vector<T>> & mat)702 matrix<T> make_matrix(const std::vector<std::vector<T>> & mat) {
703 size_t nrows = mat.size();
704 size_t ncols = mat[0].size();
705 matrix<T> ret(nrows, ncols);
706 for (size_t row = 0; row < nrows; row++)
707 for (size_t col = 0; col < nrows; col++) {
708 ret(row, col) = mat[row][col];
709 }
710 return ret;
711 }
712
713
714 template <class T>
transpose(const matrix<T> & A)715 matrix<T> transpose(const matrix<T> &A) {
716 // Transposes a Matrix
717 const size_t rows = A.GetRows(), cols = A.GetColumns();
718 matrix<T> temp(cols, rows);
719 for (size_t i = 0; i < rows; i++) {
720 for (size_t j = 0; j < cols; j++) {
721 temp(j, i) = A(i, j);
722 }
723 }
724 return temp;
725 }
726
727
728 template <class T>
dagger(const matrix<std::complex<T>> & A)729 matrix<std::complex<T>> dagger(const matrix<std::complex<T>> &A) {
730 // Take the Hermitian conjugate of a complex matrix
731 const size_t cols = A.GetColumns(), rows = A.GetRows();
732 matrix<std::complex<T>> temp(cols, rows);
733 for (size_t i = 0; i < rows; i++) {
734 for (size_t j = 0; j < cols; j++) {
735 temp(j, i) = std::conj(A(i, j));
736 }
737 }
738 return temp;
739 }
740
741
742 template <class T>
conjugate(const matrix<std::complex<T>> & A)743 matrix<std::complex<T>> conjugate(const matrix<std::complex<T>> &A) {
744 // Take the complex conjugate of a complex matrix
745 const size_t rows = A.GetRows(), cols = A.GetColumns();
746 matrix<std::complex<T>> temp(rows, cols);
747 for (size_t i = 0; i < rows; i++) {
748 for (size_t j = 0; j < cols; j++) {
749 temp(i, j) = std::conj(A(i, j));
750 }
751 }
752 return temp;
753 }
754
755 template <class T>
stacked_matrix(const std::vector<matrix<T>> & mmat)756 matrix<T> stacked_matrix(const std::vector<matrix<T>> &mmat){
757 size_t size_of_controls = mmat[0].GetRows(); // or GetColumns, as these matrices are (should be) square
758 size_t number_of_controls = mmat.size();
759
760 // Pack vector of matrices into single (stacked) matrix ... note: matrix dims: rows = (stacked_rows x size_of_controls) where:
761 // stacked_rows is the number of control matrices * the size (#rows or #columns) of each control matrix
762 // size_of_controls is the #rows (or #columns) of each control matrix
763 uint_t stacked_rows = number_of_controls*size_of_controls; // Used only for clarity in allocating the matrix
764
765 cmatrix_t stacked_matrix(stacked_rows, size_of_controls);
766 for(uint_t row = 0; row < stacked_rows; row++)
767 for(uint_t col = 0; col < size_of_controls; col++)
768 stacked_matrix(row, col) = {0.0, 0.0};
769
770 for(uint_t mmat_number = 0; mmat_number < mmat.size(); mmat_number++)
771 {
772 for(uint_t row = 0; row < size_of_controls; row++)
773 {
774 for(uint_t col = 0; col < size_of_controls; col++)
775 {
776 stacked_matrix(mmat_number * size_of_controls + row, col) = mmat[mmat_number](row, col);
777 }
778
779 }
780 }
781 return stacked_matrix;
782 }
783
784 template<class T>
matrix_diagonal(const matrix<T> & mat)785 std::vector<T> matrix_diagonal(const matrix<T>& mat) {
786 std::vector<T> vec;
787 size_t size = std::min(mat.GetRows(), mat.GetColumns());
788 vec.resize(size, 0.);
789 for (size_t i=0; i < size; i++)
790 vec[i] = mat(i, i);
791 return vec;
792 }
793
794 template <class T>
transpose_inplace(matrix<T> & A)795 matrix<T>& transpose_inplace(matrix<T> &A) {
796 // Transposes a Matrix
797 const size_t rows = A.GetRows(), cols = A.GetColumns();
798 for (size_t i = 0; i < rows; i++) {
799 for (size_t j = i + 1; j < cols; j++) {
800 const auto tmp = A(i, j);
801 A(i, j) = A(j, i);
802 A(j, i) = tmp;
803 }
804 }
805 return A;
806 }
807
808
809 template <class T>
dagger_inplace(matrix<std::complex<T>> & A)810 matrix<std::complex<T>>& dagger_inplace(matrix<std::complex<T>> &A) {
811 // Take the Hermitian conjugate of a complex matrix
812 const size_t cols = A.GetColumns(), rows = A.GetRows();
813 matrix<std::complex<T>> temp(cols, rows);
814 for (size_t i = 0; i < rows; i++) {
815 A(i, i) = conj(A(i, i));
816 for (size_t j = i + 1; j < cols; j++) {
817 const auto tmp = conj(A(i, j));
818 A(i, j) = conj(A(j, i));
819 A(j, i) = tmp;
820 }
821 }
822 return A;
823 }
824
825
826 template <class T>
conj_inplace(matrix<std::complex<T>> & A)827 matrix<std::complex<T>>& conj_inplace(matrix<std::complex<T>> &A) {
828 // Take the complex conjugate of a complex matrix
829 const size_t rows = A.GetRows(), cols = A.GetColumns();
830 for (size_t i = 0; i < rows; i++) {
831 for (size_t j = 0; j < cols; j++) {
832 A(i, j) = conj(A(i, j));
833 }
834 }
835 return A;
836 }
837
838
839 template <class T>
trace(const matrix<T> & A)840 T trace(const matrix<T> &A) {
841 // Finds the trace of a matrix
842 size_t rows = A.GetRows(), cols = A.GetColumns();
843 if (rows != cols) {
844 throw std::invalid_argument("MU::trace: matrix is not square");
845 }
846 T temp = 0.0;
847 for (size_t i = 0; i < rows; i++) {
848 temp = temp + A(i, i);
849 }
850 return temp;
851 }
852
853 template <class T>
partial_trace_a(const matrix<T> & rho,size_t dimA)854 matrix<T> partial_trace_a(const matrix<T> &rho, size_t dimA) {
855 // Traces out first system (dimension dimA) of composite Hilbert space
856 size_t rows = rho.GetRows(), cols = rho.GetColumns();
857 if (rows != cols) {
858 throw std::invalid_argument("MU::partial_trace_a: matrix is not square");
859 }
860 if (rows % dimA != 0) {
861 throw std::invalid_argument("MU::partial_trace_a: dim(rho)/dim(system b) is not an integer");
862 }
863 size_t dimB = rows / dimA;
864 matrix<T> rhoB(dimB, dimB);
865 T temp = 0.0;
866 for (size_t i = 0; i < dimB; i++) {
867 for (size_t j = 0; j < dimB; j++) {
868 for (size_t k = 0; k < dimA; k++) {
869 temp = temp + rho(i + dimB * k, j + dimB * k);
870 }
871 rhoB(i, j) = temp;
872 temp = 0.0;
873 }
874 }
875 return rhoB;
876 }
877
878
879 template <class T>
partial_trace_b(const matrix<T> & rho,size_t dimB)880 matrix<T> partial_trace_b(const matrix<T> &rho, size_t dimB) {
881 // Traces out second system (dimension dimB) of composite Hilbert space
882 size_t rows = rho.GetRows(), cols = rho.GetColumns();
883 if (rows != cols) {
884 throw std::invalid_argument("MU::partial_trace_b: matrix is not square");
885 }
886 if (rows % dimB != 0) {
887 throw std::invalid_argument("MU::partial_trace_b: dim(rho)/dim(system a) is not an integer");
888 }
889 size_t dimA = rows / dimB;
890 matrix<T> rhoA(dimA, dimA);
891 T temp = 0.0;
892 for (size_t i = 0; i < dimA; i++) {
893 size_t offsetX = i * dimB;
894 for (size_t j = 0; j < dimA; j++) {
895 size_t offsetY = j * dimB;
896 for (size_t k = 0; k < dimB; k++) {
897 temp = temp + rho(offsetX + k, offsetY + k);
898 }
899 rhoA(i, j) = temp;
900 temp = 0.0;
901 }
902 }
903 return rhoA;
904 }
905
906
907 template <class T>
tensor_product(const matrix<T> & A,const matrix<T> & B)908 matrix<T> tensor_product(const matrix<T> &A, const matrix<T> &B) {
909 // Works out the TensorProduct of two matricies A tensor B
910 // Note that if A is i x j and B is p x q then A \otimes B is an ip x jq
911 // rmatrix
912
913 // If A or B is empty it will return the other matrix
914 if (A.size() == 0)
915 return B;
916 if (B.size() == 0)
917 return A;
918
919 size_t rows1 = A.GetRows(), rows2 = B.GetRows(), cols1 = A.GetColumns(),
920 cols2 = B.GetColumns();
921 size_t rows_new = rows1 * rows2, cols_new = cols1 * cols2, n, m;
922 matrix<T> temp(rows_new, cols_new);
923 // a11 B, a12 B ... a1j B
924 // ai1 B, ai2 B ... aij B
925 for (size_t i = 0; i < rows1; i++) {
926 for (size_t j = 0; j < cols1; j++) {
927 for (size_t p = 0; p < rows2; p++) {
928 for (size_t q = 0; q < cols2; q++) {
929 n = i * rows2 + p;
930 m = j * cols2 + q; // 0 (0 + 1) + 1*dimb=2 + (0 + 1 ) (j*dimb+q)
931 temp(n, m) = A(i, j) * B(p, q);
932 }
933 }
934 }
935 }
936 return temp;
937 }
938
unitary_superop(const matrix<T> & mat)939 template <class T> matrix<T> unitary_superop(const matrix<T> &mat) {
940 return tensor_product(conjugate(mat), mat);
941 }
942
943 template <class T>
concatenate(const matrix<T> & A,const matrix<T> & B,uint_t axis)944 matrix<T> concatenate (const matrix<T> &A, const matrix<T> &B, uint_t axis) {
945 if (axis != 0 && axis!= 1) {
946 throw std::invalid_argument("Utils::concatenate: axis must be 0 or 1");
947 }
948 size_t rows1 = A.GetRows(), rows2 = B.GetRows(), cols1 = A.GetColumns(), cols2 = B.GetColumns();
949 matrix<T> temp = A;
950 if(axis == 0) {
951 if(cols1 != cols2) {
952 throw std::invalid_argument("Utils::concatenate: axis must be 0 or 1");
953 }
954 temp.resize(rows1 + rows2, cols1);
955 for (size_t i = 0; i < rows2; i++)
956 for (size_t j = 0; j < cols1; j++)
957 temp(rows1 + i,j) = B(i,j);
958 }
959 else if(axis == 1) {
960 if(rows1 != rows2) {
961 throw std::invalid_argument("Utils::concatenate: the 2 matrices have a different number of rows");
962 }
963 temp.resize(rows1, cols1 + cols2);
964 for (size_t i = 0; i < rows1; i++)
965 for (size_t j = 0; j < cols2; j++)
966 temp(i,cols1 + j) = B(i,j);
967 }
968 return temp;
969 }
970
971 template <class T>
split(const matrix<T> & A,matrix<T> & B,matrix<T> & C,uint_t axis)972 void split (const matrix<T> &A, matrix<T> &B, matrix<T> &C, uint_t axis) {
973 if (axis != 0 && axis != 1) {
974 throw std::invalid_argument("Utils::split: axis must be 0 or 1");
975 }
976 size_t rows = A.GetRows(), cols = A.GetColumns();
977 matrix<T> temp = A;
978 if(axis == 0) {
979 if (rows % 2 != 0) {
980 throw std::invalid_argument("Utils::split: can't split matrix A by rows");
981 }
982 B.resize(rows/2 , cols);
983 C.resize(rows/2 , cols);
984 for (size_t i = 0; i < rows/2; i++) {
985 for (size_t j = 0; j < cols; j++) {
986 B(i,j) = A(i,j);
987 C(i,j) = A(i+rows/2,j);
988 }
989 }
990 }
991 else if(axis == 1) {
992 if (cols % 2 != 0) {
993 throw std::invalid_argument("Utils::split: can't split matrix A by columns");
994 }
995 B.resize(rows, cols/2);
996 C.resize(rows, cols/2);
997 for (size_t i = 0; i < rows; i++){
998 for (size_t j = 0; j < cols/2; j++) {
999 B(i,j) = A(i,j);
1000 C(i,j) = A(i,j+cols/2);
1001 }
1002 }
1003 }
1004 }
1005
1006 template <class T>
elementwise_multiplication(const matrix<T> & A,const matrix<T> & B)1007 matrix<T> elementwise_multiplication(const matrix<T> &A, const matrix<T> &B) {
1008 // Works out an elementwise multiplication of two matrices A, B
1009 // If A or B is empty it will return the other matrix
1010 size_t rows1 = A.GetRows(), rows2 = B.GetRows(), cols1 = A.GetColumns(),
1011 cols2 = B.GetColumns();
1012 if(rows1 != rows2 || cols1 != cols2) {
1013 throw std::invalid_argument("Utils::elementwise_multiplication: matrices have different sizes");
1014 }
1015 matrix<T> temp(rows1, cols1);
1016 for (size_t i = 0; i < rows1; i++)
1017 for (size_t j = 0; j < cols1; j++)
1018 temp(i, j) = A(i, j) * B(i, j);
1019 return temp;
1020 }
1021
1022 template <class T>
sum(const matrix<T> & A)1023 T sum(const matrix<T> &A){
1024 T temp = 0;
1025 for(uint_t i = 0; i < A.size(); i++)
1026 temp += A[i];
1027 return temp;
1028 }
1029
1030
1031 template <class T>
is_square(const matrix<T> & mat)1032 bool is_square(const matrix<T> &mat) {
1033 if (mat.GetRows() != mat.GetColumns())
1034 return false;
1035 return true;
1036 }
1037
1038 template <class T>
is_diagonal(const matrix<T> & mat)1039 bool is_diagonal(const matrix<T> &mat) {
1040 // Check if row-matrix for diagonal
1041 if (mat.GetRows() == 1 && mat.GetColumns() > 0)
1042 return true;
1043 return false;
1044 }
1045
1046 template <class T>
is_equal(const matrix<T> & mat1,const matrix<T> & mat2,double threshold)1047 bool is_equal(const matrix<T> &mat1, const matrix<T> &mat2, double threshold) {
1048
1049 // Check matrices are same shape
1050 const auto nrows = mat1.GetRows();
1051 const auto ncols = mat1.GetColumns();
1052 if (nrows != mat2.GetRows() || ncols != mat2.GetColumns())
1053 return false;
1054
1055 // Check matrices are equal on an entry by entry basis
1056 double delta = 0;
1057 for (size_t i=0; i < nrows; i++) {
1058 for (size_t j=0; j < ncols; j++) {
1059 delta += std::real(std::abs(mat1(i, j) - mat2(i, j)));
1060 }
1061 }
1062 return (delta < threshold);
1063 }
1064
1065 template <class T>
is_diagonal(const matrix<T> & mat,double threshold)1066 bool is_diagonal(const matrix<T> &mat, double threshold) {
1067 // Check U matrix is identity
1068 const auto nrows = mat.GetRows();
1069 const auto ncols = mat.GetColumns();
1070 if (nrows != ncols)
1071 return false;
1072 for (size_t i=0; i < nrows; i++)
1073 for (size_t j=0; j < ncols; j++)
1074 if (i != j && std::real(std::abs(mat(i, j))) > threshold)
1075 return false;
1076 return true;
1077 }
1078
1079
1080 template <class T>
is_identity_phase(const matrix<T> & mat,double threshold)1081 std::pair<bool, double> is_identity_phase(const matrix<T> &mat, double threshold) {
1082
1083 // To check if identity we first check we check that:
1084 // 1. U(0,0) = exp(i * theta)
1085 // 2. U(i, i) = U(0, 0)
1086 // 3. U(i, j) = 0 for j != i
1087 auto failed = std::make_pair(false, 0.0);
1088
1089 // Check condition 1.
1090 const auto u00 = mat(0, 0);
1091 //if (std::norm(std::abs(u00) - 1.0) > threshold)
1092 // return failed;
1093 if (std::norm(std::abs(u00) - 1.0) > threshold) {
1094 return failed;
1095 }
1096 const auto theta = std::arg(u00);
1097
1098 // Check conditions 2 and 3
1099 double delta = 0.;
1100 const auto nrows = mat.GetRows();
1101 const auto ncols = mat.GetColumns();
1102 if (nrows != ncols)
1103 return failed;
1104 for (size_t i=0; i < nrows; i++) {
1105 for (size_t j=0; j < ncols; j++) {
1106 auto val = (i==j) ? std::norm(mat(i, j) - u00)
1107 : std::norm(mat(i, j));
1108 if (val > threshold) {
1109 return failed; // fail fast if single entry differs
1110 } else
1111 delta += val; // accumulate difference
1112 }
1113 }
1114 // Check small errors didn't accumulate
1115 if (delta > threshold) {
1116 return failed;
1117 }
1118 // Otherwise we pass
1119 return std::make_pair(true, theta);
1120 }
1121
1122 template <class T>
is_identity(const matrix<T> & mat,double threshold)1123 bool is_identity(const matrix<T> &mat, double threshold) {
1124 // Check mat(0, 0) == 1
1125 if (std::norm(mat(0, 0) - T(1)) > threshold)
1126 return false;
1127 // If this passes now use is_identity_phase (and we know
1128 // phase will be zero).
1129 return is_identity_phase(mat, threshold).first;
1130 }
1131
1132 template <class T>
is_diagonal_identity(const matrix<T> & mat,double threshold)1133 bool is_diagonal_identity(const matrix<T> &mat, double threshold) {
1134 // Check U matrix is identity
1135 if (is_diagonal(mat, threshold) == false)
1136 return false;
1137 double delta = 0.;
1138 const auto ncols = mat.GetColumns();
1139 for (size_t j=0; j < ncols; j++) {
1140 delta += std::real(std::abs(mat(0, j) - 1.0));
1141 }
1142 return (delta < threshold);
1143 }
1144
1145 template <class T>
is_unitary(const matrix<T> & mat,double threshold)1146 bool is_unitary(const matrix<T> &mat, double threshold) {
1147 size_t nrows = mat.GetRows();
1148 size_t ncols = mat.GetColumns();
1149 // Check if diagonal row-matrix
1150 if (nrows == 1) {
1151 for (size_t j=0; j < ncols; j++) {
1152 double delta = std::abs(1.0 - std::real(std::abs(mat(0, j))));
1153 if (delta > threshold)
1154 return false;
1155 }
1156 return true;
1157 }
1158 // Check U matrix is square
1159 if (nrows != ncols)
1160 return false;
1161 // Check U matrix is unitary
1162 const matrix<T> check = mat * dagger(mat);
1163 return is_identity(check, threshold);
1164 }
1165
1166
1167 template <class T>
is_hermitian_matrix(const matrix<T> & mat,double threshold)1168 bool is_hermitian_matrix(const matrix<T> &mat, double threshold) {
1169 return is_equal(mat, dagger(mat), threshold);
1170 }
1171
1172 template <class T>
is_symmetrix(const matrix<T> & mat,double threshold)1173 bool is_symmetrix(const matrix<T> &mat, double threshold) {
1174 return is_equal(mat, transpose(mat), threshold);
1175 }
1176
1177 template <class T>
is_cptp_kraus(const std::vector<matrix<T>> & mats,double threshold)1178 bool is_cptp_kraus(const std::vector<matrix<T>> &mats, double threshold) {
1179 matrix<T> cptp(mats[0].size());
1180 for (const auto &mat : mats) {
1181 cptp = cptp + dagger(mat) * mat;
1182 }
1183 return is_identity(cptp, threshold);
1184 }
1185
1186 //==============================================================================
1187 // Implementations: Vector functions
1188 //==============================================================================
1189
1190 template <class T>
is_unit_vector(const std::vector<T> & vec,double threshold)1191 bool is_unit_vector(const std::vector<T> &vec, double threshold) {
1192 return (std::abs(norm<T>(vec) - 1.0) < threshold);
1193 }
1194
1195 template <typename T>
conjugate(const std::vector<std::complex<T>> & v)1196 std::vector<std::complex<T>> conjugate(const std::vector<std::complex<T>> &v) {
1197 std::vector<std::complex<T>> ret;
1198 std::transform(v.cbegin(), v.cend(), std::back_inserter(ret),
1199 [] (const std::complex<T> &c) -> std::complex<T> { return std::conj(c); });
1200 return ret;
1201 }
1202
1203 template <typename T>
norm(const std::vector<T> & vec)1204 double norm(const std::vector<T> &vec) {
1205 double val = 0.0;
1206 for (const auto v : vec) {
1207 val += std::real(v * std::conj(v));
1208 }
1209 return std::sqrt(val);
1210 }
1211
1212 template <typename T>
outer_product(const std::vector<T> & ket,const std::vector<T> & bra)1213 matrix<T> outer_product(const std::vector<T> &ket, const std::vector<T> &bra) {
1214 const uint_t d1 = ket.size();
1215 const uint_t d2 = bra.size();
1216 matrix<T> ret(d1, d2);
1217 for (uint_t i = 0; i < d1; i++)
1218 for (uint_t j = 0; j < d2; j++) {
1219 ret(i, j) = ket[i] * std::conj(bra[j]);
1220 }
1221 return ret;
1222 }
1223
1224 template <typename T>
tensor_product(const std::vector<T> & vec1,const std::vector<T> & vec2)1225 std::vector<T> tensor_product(const std::vector<T> &vec1,
1226 const std::vector<T> &vec2) {
1227 std::vector<T> ret;
1228 ret.reserve(vec1.size() * vec2.size());
1229 for (const auto &a : vec1)
1230 for (const auto &b : vec2) {
1231 ret.push_back(a * b);
1232 }
1233 return ret;
1234 }
1235
1236 template <typename T1, typename T2>
scalar_multiply(const std::vector<T1> & vec,T2 val)1237 std::vector<T1> scalar_multiply(const std::vector<T1> &vec, T2 val) {
1238 std::vector<T1> ret;
1239 ret.reserve(vec.size());
1240 for (const auto &elt : vec) {
1241 ret.push_back(val * elt);
1242 }
1243 return ret;
1244 }
1245
1246
1247 template <typename T1, typename T2>
scalar_multiply_inplace(std::vector<T1> & vec,T2 val)1248 std::vector<T1>& scalar_multiply_inplace(std::vector<T1> &vec, T2 val) {
1249 for (auto &elt : vec) {
1250 elt = val * elt; // use * incase T1 doesn't have *= method
1251 }
1252 return vec;
1253 }
1254
1255
chop_inplace(double & val,double epsilon)1256 double &chop_inplace(double &val, double epsilon) {
1257 if (std::abs(val) < epsilon)
1258 val = 0.;
1259 return val;
1260 }
1261
1262
chop_inplace(std::complex<double> & val,double epsilon)1263 std::complex<double> &chop_inplace(std::complex<double> &val, double epsilon) {
1264 val.real(chop(val.real(), epsilon));
1265 val.imag(chop(val.imag(), epsilon));
1266 return val;
1267 }
1268
1269
1270 template <typename T>
chop_inplace(std::vector<T> & vec,double epsilon)1271 std::vector<T> &chop_inplace(std::vector<T> &vec, double epsilon) {
1272 if (epsilon > 0.)
1273 for (auto &v : vec)
1274 chop_inplace(v, epsilon);
1275 return vec;
1276 }
1277
1278
chop(double val,double epsilon)1279 double chop(double val, double epsilon) {
1280 return (std::abs(val) < epsilon) ? 0. : val;
1281 }
1282
1283
1284 template <typename T>
chop(std::complex<T> val,double epsilon)1285 std::complex<T> chop(std::complex<T> val, double epsilon) {
1286 return {chop(val.real(), epsilon), chop(val.imag(), epsilon)};
1287 }
1288
1289
1290 template <typename T>
chop(const std::vector<T> & vec,double epsilon)1291 std::vector<T> chop(const std::vector<T> &vec, double epsilon) {
1292 std::vector<T> tmp;
1293 tmp.reserve(vec.size());
1294 for (const auto &v : vec)
1295 tmp.push_back(chop(v, epsilon));
1296 return tmp;
1297 }
1298
1299
1300 template <class T>
combine(std::vector<T> & lhs,const std::vector<T> & rhs)1301 void combine(std::vector<T> &lhs, const std::vector<T> &rhs) {
1302 // if lhs is empty, set it to be rhs vector
1303 if (lhs.size() == 0) {
1304 lhs = rhs;
1305 return;
1306 }
1307 // if lhs is not empty rhs must be same size
1308 if (lhs.size() != rhs.size()) {
1309 throw std::invalid_argument("Utils::combine (vectors are not same length.)");
1310 }
1311 for (size_t j=0; j < lhs.size(); ++j) {
1312 lhs[j] += rhs[j];
1313 }
1314 }
1315
1316
1317 template <typename T>
vec2ket(const std::vector<T> & vec,double epsilon,uint_t base)1318 std::map<std::string, T> vec2ket(const std::vector<T> &vec, double epsilon, uint_t base) {
1319
1320 bool hex_output = false;
1321 if (base == 16) {
1322 hex_output = true;
1323 base = 2; // If hexadecimal strings we convert to bin first
1324 }
1325 // check vector length
1326 size_t dim = vec.size();
1327 double n = std::log(dim) / std::log(base);
1328 uint_t nint = std::trunc(n);
1329 if (std::abs(nint - n) > 1e-5) {
1330 std::stringstream ss;
1331 ss << "vec2ket (vector dimension " << dim << " is not of size " << base << "^n)";
1332 throw std::invalid_argument(ss.str());
1333 }
1334 std::map<std::string, T> ketmap;
1335 for (size_t k = 0; k < dim; ++k) {
1336 T val = chop(vec[k], epsilon);
1337 if (std::abs(val) > epsilon) {
1338 std::string key = (hex_output) ? Utils::int2hex(k)
1339 : Utils::int2string(k, base, nint);
1340 ketmap.insert({key, val});
1341 }
1342 }
1343 return ketmap;
1344 }
1345
1346
1347 //==============================================================================
1348 // Implementations: Bit conversions
1349 //==============================================================================
1350
format_hex_inplace(std::string & hex)1351 std::string& format_hex_inplace(std::string &hex) {
1352 // make abcdef and x lower case
1353 std::transform(hex.begin(), hex.end(), hex.begin(), ::tolower);
1354 // check if 0x prefix is present, add if it isn't
1355 std::string prefix = hex.substr(0, 2);
1356 if (prefix != "0x")
1357 hex = "0x" + hex;
1358 // delete leading zeros Eg 0x001 -> 0x1
1359 hex.erase(2, std::min(hex.find_first_not_of("0", 2) - 2, hex.size() - 3));
1360 return hex;
1361 }
1362
1363
format_hex(const std::string & hex)1364 std::string format_hex(const std::string &hex) {
1365 std::string tmp = hex;
1366 format_hex_inplace(tmp);
1367 return tmp;
1368 }
1369
1370
padleft_inplace(std::string & s,char c,size_t min_length)1371 std::string& padleft_inplace(std::string &s, char c, size_t min_length) {
1372 auto l = s.size();
1373 if (l < min_length)
1374 s = std::string(min_length - l, c) + s;
1375 return s;
1376 }
1377
1378
padleft(const std::string & s,char c,size_t min_length)1379 std::string padleft(const std::string &s, char c, size_t min_length) {
1380 std::string tmp = s;
1381 return padleft_inplace(tmp, c, min_length);
1382 }
1383
1384
int2reg(uint_t n,uint_t base)1385 reg_t int2reg(uint_t n, uint_t base) {
1386 reg_t ret;
1387 while (n >= base) {
1388 ret.push_back(n % base);
1389 n /= base;
1390 }
1391 ret.push_back(n); // last case n < base;
1392 return ret;
1393 }
1394
1395
int2reg(uint_t n,uint_t base,uint_t minlen)1396 reg_t int2reg(uint_t n, uint_t base, uint_t minlen) {
1397 reg_t ret = int2reg(n, base);
1398 if (ret.size() < minlen) // pad vector with zeros
1399 ret.resize(minlen);
1400 return ret;
1401 }
1402
1403
hex2reg(std::string str)1404 reg_t hex2reg(std::string str) {
1405 reg_t reg;
1406 std::string prefix = str.substr(0, 2);
1407 if (prefix == "0x" || prefix == "0X") { // Hexadecimal
1408 str.erase(0, 2); // remove '0x';
1409 size_t length = (str.size() % 8) + 32 * (str.size() / 8);
1410 reg.reserve(length);
1411 while (str.size() > 8) {
1412 unsigned long hex = stoull(str.substr(str.size() - 8), nullptr, 16);
1413 reg_t tmp = int2reg(hex, 2, 32);
1414 std::move(tmp.begin(), tmp.end(), back_inserter(reg));
1415 str.erase(str.size() - 8);
1416 }
1417 if (str.size() > 0) {
1418 reg_t tmp = int2reg(stoul(str, nullptr, 16), 2, 0);
1419 std::move(tmp.begin(), tmp.end(), back_inserter(reg));
1420 }
1421 return reg;
1422 } else {
1423 throw std::runtime_error(std::string("invalid hexadecimal"));
1424 }
1425 }
1426
1427
hex2bin(std::string str,bool prefix)1428 std::string hex2bin(std::string str, bool prefix) {
1429 // empty case
1430 if (str.empty())
1431 return std::string();
1432
1433 // If string starts with 0b prob prefix
1434 if (str.size() > 1 && str.substr(0, 2) == "0x") {
1435 str.erase(0, 2);
1436 }
1437
1438 // We go via long integer conversion, so we process 64-bit chunks at
1439 // a time
1440 const size_t block = 8;
1441 const size_t len = str.size();
1442 const size_t chunks = len / block;
1443 const size_t remain = len % block;
1444
1445 // Initialize output string
1446 std::string bin = (prefix) ? "0b" : "";
1447
1448 // Start with remain
1449 bin += int2string(std::stoull(str.substr(0, remain), nullptr, 16), 2);
1450 for (size_t j=0; j < chunks; ++j) {
1451 std::string part = int2string(std::stoull(str.substr(remain + j * block, block), nullptr, 16), 2, 64);
1452 bin += part;
1453 }
1454 return bin;
1455 }
1456
1457
bin2hex(std::string str,bool prefix)1458 std::string bin2hex(std::string str, bool prefix) {
1459 // empty case
1460 if (str.empty())
1461 return std::string();
1462
1463 // If string starts with 0b prob prefix
1464 if (str.size() > 1 && str.substr(0, 2) == "0b") {
1465 str.erase(0, 2);
1466 }
1467
1468 // We go via long integer conversion, so we process 64-bit chunks at
1469 // a time
1470 const size_t bin_block = 64;
1471 const size_t hex_block = bin_block / 4;
1472 const size_t len = str.size();
1473 const size_t chunks = len / bin_block;
1474 const size_t remain = len % bin_block;
1475
1476 // initialize output string
1477 std::string hex = (prefix) ? "0x" : "";
1478
1479 // Add remainder
1480 if (remain > 0) {
1481 // Add remainder
1482 std::stringstream ss;
1483 ss << std::hex << std::stoull(str.substr(0, remain), nullptr, 2);
1484 hex += ss.str();
1485 }
1486
1487 // Add > 64 bit chunks
1488 if (chunks > 0) {
1489 // Add last 64-bit chunk
1490 std::stringstream ss;
1491 ss << std::hex << std::stoull(str.substr(remain, bin_block), nullptr, 2);
1492 std::string part = ss.str();
1493 if (remain > 0) {
1494 part.insert(0, hex_block - part.size(), '0'); // pad out zeros
1495 }
1496 hex += part;
1497 // Add any additional chunks
1498 for (size_t j=1; j < chunks; ++j) {
1499 ss = std::stringstream(); // clear string stream
1500 ss << std::hex << std::stoull(str.substr(remain + j * bin_block, bin_block), nullptr, 2);
1501 part = ss.str();
1502 part.insert(0, hex_block - part.size(), '0');
1503 hex += part;
1504 }
1505 }
1506 return hex;
1507 }
1508
1509
reg2int(const reg_t & reg,uint_t base)1510 uint_t reg2int(const reg_t ®, uint_t base) {
1511 uint_t ret = 0;
1512 if (base == 2) {
1513 // For base-2 use bit-shifting
1514 for (size_t j=0; j < reg.size(); j++)
1515 if (reg[j])
1516 ret += (1ULL << j);
1517 } else {
1518 // For other bases use exponentiation
1519 for (size_t j=0; j < reg.size(); j++)
1520 if (reg[j] > 0)
1521 ret += reg[j] * static_cast<uint_t>(pow(base, j));
1522 }
1523 return ret;
1524 }
1525
1526
int2string(uint_t n,uint_t base)1527 std::string int2string(uint_t n, uint_t base) {
1528 if (base < 2 || base > 10) {
1529 throw std::invalid_argument("Utils::int2string base must be between 2 and 10.");
1530 }
1531 if (n < base)
1532 return std::to_string(n);
1533 else
1534 return int2string(n / base, base) + std::to_string(n % base);
1535 }
1536
1537
int2string(uint_t n,uint_t base,uint_t minlen)1538 std::string int2string(uint_t n, uint_t base, uint_t minlen) {
1539 std::string tmp = int2string(n, base);
1540 return padleft_inplace(tmp, '0', minlen);
1541 }
1542
1543
1544 //------------------------------------------------------------------------------
1545 } // end namespace Utils
1546 //------------------------------------------------------------------------------
1547 } // end namespace AER
1548 //------------------------------------------------------------------------------
1549 #endif