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 &reg, 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 &reg, 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