1 /*
2  * This file is part of Quantum++.
3  *
4  * Copyright (c) 2013 - 2021 softwareQ Inc. All rights reserved.
5  *
6  * MIT License
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights
11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12  * copies of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24  * SOFTWARE.
25  */
26 
27 /**
28  * \file functions.hpp
29  * \brief Generic quantum computing functions
30  */
31 
32 #ifndef FUNCTIONS_HPP_
33 #define FUNCTIONS_HPP_
34 
35 namespace qpp {
36 // Eigen function wrappers
37 /**
38  * \brief Transpose
39  *
40  * \param A Eigen expression
41  * \return Transpose of \a A, as a dynamic matrix over the same scalar field as
42  * \a A
43  */
44 template <typename Derived>
45 dyn_mat<typename Derived::Scalar>
transpose(const Eigen::MatrixBase<Derived> & A)46 transpose(const Eigen::MatrixBase<Derived>& A) {
47     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
48 
49     // EXCEPTION CHECKS
50 
51     // check zero-size
52     if (!internal::check_nonzero_size(rA))
53         throw exception::ZeroSize("qpp::transpose()");
54     // END EXCEPTION CHECKS
55 
56     return rA.transpose();
57 }
58 
59 /**
60  * \brief Complex conjugate
61  *
62  * \param A Eigen expression
63  * \return Complex conjugate of \a A, as a dynamic matrix over the same scalar
64  * field as \a A
65  */
66 template <typename Derived>
67 dyn_mat<typename Derived::Scalar>
conjugate(const Eigen::MatrixBase<Derived> & A)68 conjugate(const Eigen::MatrixBase<Derived>& A) {
69     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
70 
71     // EXCEPTION CHECKS
72 
73     // check zero-size
74     if (!internal::check_nonzero_size(rA))
75         throw exception::ZeroSize("qpp::conjugate()");
76     // END EXCEPTION CHECKS
77 
78     return rA.conjugate();
79 }
80 
81 /**
82  * \brief Adjoint
83  *
84  * \param A Eigen expression
85  * \return Adjoint (Hermitian conjugate) of \a A, as a dynamic matrix over the
86  * same scalar field as \a A
87  */
88 template <typename Derived>
adjoint(const Eigen::MatrixBase<Derived> & A)89 dyn_mat<typename Derived::Scalar> adjoint(const Eigen::MatrixBase<Derived>& A) {
90     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
91 
92     // EXCEPTION CHECKS
93 
94     // check zero-size
95     if (!internal::check_nonzero_size(rA))
96         throw exception::ZeroSize("qpp::adjoint()");
97     // END EXCEPTION CHECKS
98 
99     return rA.adjoint();
100 }
101 
102 /**
103  * \brief Inverse
104  *
105  * \param A Eigen expression
106  * \return Inverse of \a A, as a dynamic matrix over the same scalar field as
107  * \a A
108  */
109 template <typename Derived>
inverse(const Eigen::MatrixBase<Derived> & A)110 dyn_mat<typename Derived::Scalar> inverse(const Eigen::MatrixBase<Derived>& A) {
111     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
112 
113     // EXCEPTION CHECKS
114 
115     // check zero-size
116     if (!internal::check_nonzero_size(rA))
117         throw exception::ZeroSize("qpp::inverse()");
118 
119     // check square matrix
120     if (!internal::check_square_mat(rA))
121         throw exception::MatrixNotSquare("qpp::inverse()");
122     // END EXCEPTION CHECKS
123 
124     return rA.inverse();
125 }
126 
127 /**
128  * \brief Trace
129  *
130  * \param A Eigen expression
131  * \return Trace of \a A, as a scalar over the same scalar field as \a A
132  */
133 template <typename Derived>
trace(const Eigen::MatrixBase<Derived> & A)134 typename Derived::Scalar trace(const Eigen::MatrixBase<Derived>& A) {
135     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
136 
137     // EXCEPTION CHECKS
138 
139     // check zero-size
140     if (!internal::check_nonzero_size(rA))
141         throw exception::ZeroSize("qpp::trace()");
142 
143     // check square matrix
144     if (!internal::check_square_mat(rA))
145         throw exception::MatrixNotSquare("qpp::trace()");
146     // END EXCEPTION CHECKS
147 
148     return rA.trace();
149 }
150 
151 /**
152  * \brief Determinant
153  *
154  * \param A Eigen expression
155  * \return Determinant of \a A, as a scalar over the same scalar field as \a A.
156  * Returns \f$\pm \infty\f$ when the determinant overflows/underflows.
157  */
158 template <typename Derived>
det(const Eigen::MatrixBase<Derived> & A)159 typename Derived::Scalar det(const Eigen::MatrixBase<Derived>& A) {
160     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
161 
162     // EXCEPTION CHECKS
163 
164     // check zero-size
165     if (!internal::check_nonzero_size(rA))
166         throw exception::ZeroSize("qpp::det()");
167 
168     // check square matrix
169     if (!internal::check_square_mat(rA))
170         throw exception::MatrixNotSquare("qpp::det()");
171     // END EXCEPTION CHECKS
172 
173     return rA.determinant();
174 }
175 
176 /**
177  * \brief Logarithm of the determinant
178  *
179  * Useful when the determinant overflows/underflows
180  *
181  * \param A Eigen expression
182  * \return Logarithm of the determinant of \a A, as a scalar over the same
183  * scalar field as \a A
184  */
185 template <typename Derived>
logdet(const Eigen::MatrixBase<Derived> & A)186 typename Derived::Scalar logdet(const Eigen::MatrixBase<Derived>& A) {
187     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
188 
189     // EXCEPTION CHECKS
190 
191     // check zero-size
192     if (!internal::check_nonzero_size(rA))
193         throw exception::ZeroSize("qpp::logdet()");
194 
195     // check square matrix
196     if (!internal::check_square_mat(rA))
197         throw exception::MatrixNotSquare("qpp::logdet()");
198     // END EXCEPTION CHECKS
199 
200     Eigen::PartialPivLU<dyn_mat<typename Derived::Scalar>> lu(rA);
201     dyn_mat<typename Derived::Scalar> U =
202         lu.matrixLU().template triangularView<Eigen::Upper>();
203     typename Derived::Scalar result = std::log(U(0, 0));
204 
205     for (idx i = 1; i < static_cast<idx>(rA.rows()); ++i)
206         result += std::log(U(i, i));
207 
208     return result;
209 }
210 
211 /**
212  * \brief Element-wise sum of \a A
213  *
214  * \param A Eigen expression
215  * \return Element-wise sum of \a A, as a scalar over the same scalar field as
216  * \a A
217  */
218 template <typename Derived>
sum(const Eigen::MatrixBase<Derived> & A)219 typename Derived::Scalar sum(const Eigen::MatrixBase<Derived>& A) {
220     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
221 
222     // EXCEPTION CHECKS
223 
224     // check zero-size
225     if (!internal::check_nonzero_size(rA))
226         throw exception::ZeroSize("qpp::sum()");
227     // END EXCEPTION CHECKS
228 
229     return rA.sum();
230 }
231 
232 /**
233  * \brief Element-wise product of \a A
234  *
235  * \param A Eigen expression
236  * \return Element-wise product of \a A, as a scalar over the same scalar field
237  * as \a A
238  */
239 template <typename Derived>
prod(const Eigen::MatrixBase<Derived> & A)240 typename Derived::Scalar prod(const Eigen::MatrixBase<Derived>& A) {
241     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
242 
243     // EXCEPTION CHECKS
244 
245     // check zero-size
246     if (!internal::check_nonzero_size(rA))
247         throw exception::ZeroSize("qpp::prod()");
248     // END EXCEPTION CHECKS
249 
250     return rA.prod();
251 }
252 
253 /**
254  * \brief Frobenius norm
255  *
256  * \param A Eigen expression
257  * \return Frobenius norm of \a A
258  */
259 template <typename Derived>
norm(const Eigen::MatrixBase<Derived> & A)260 double norm(const Eigen::MatrixBase<Derived>& A) {
261     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
262 
263     // EXCEPTION CHECKS
264 
265     // check zero-size
266     if (!internal::check_nonzero_size(rA))
267         throw exception::ZeroSize("qpp::norm()");
268     // END EXCEPTION CHECKS
269 
270     // convert matrix to complex then return its norm
271     return (rA.template cast<cplx>()).norm();
272 }
273 
274 /**
275  * \brief Normalizes state vector (column or row vector) or density matrix
276  *
277  * \param A Eigen expression
278  * \return Normalized state vector or density matrix
279  */
280 template <typename Derived>
281 dyn_mat<typename Derived::Scalar>
normalize(const Eigen::MatrixBase<Derived> & A)282 normalize(const Eigen::MatrixBase<Derived>& A) {
283     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
284 
285     // EXCEPTION CHECKS
286 
287     // check zero size
288     if (!internal::check_nonzero_size(rA))
289         throw exception::ZeroSize("qpp::normalize()");
290     // END EXCEPTION CHECKS
291 
292     dyn_mat<typename Derived::Scalar> result;
293 
294     if (internal::check_cvector(rA) || internal::check_rvector(rA)) {
295         double normA = norm(rA);
296         if (normA == 0) {
297             throw std::overflow_error("qpp::normalize(): Division by zero!");
298         }
299         result = rA / normA;
300     } else if (internal::check_square_mat(rA)) {
301         typename Derived::Scalar traceA = trace(rA);
302         if (std::abs(traceA) == 0) {
303             throw std::overflow_error("qpp::normalize(): Division by zero!");
304         }
305         result = rA / trace(rA);
306     } else
307         throw exception::MatrixNotSquareNorVector("qpp::normalize()");
308 
309     return result;
310 }
311 
312 /**
313  * \brief Full eigen decomposition
314  * \see qpp::heig()
315  *
316  * \param A Eigen expression
317  * \return Pair of: 1. Eigenvalues of \a A, as a complex dynamic column vector,
318  * and 2. Eigenvectors of \a A, as columns of a complex dynamic matrix
319  */
320 template <typename Derived>
eig(const Eigen::MatrixBase<Derived> & A)321 std::pair<dyn_col_vect<cplx>, cmat> eig(const Eigen::MatrixBase<Derived>& A) {
322     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
323 
324     // EXCEPTION CHECKS
325 
326     // check zero-size
327     if (!internal::check_nonzero_size(rA))
328         throw exception::ZeroSize("qpp::eig()");
329 
330     // check square matrix
331     if (!internal::check_square_mat(rA))
332         throw exception::MatrixNotSquare("qpp::eig()");
333     // END EXCEPTION CHECKS
334 
335     Eigen::ComplexEigenSolver<cmat> es(rA.template cast<cplx>());
336 
337     return std::make_pair(es.eigenvalues(), es.eigenvectors());
338 }
339 
340 /**
341  * \brief Eigenvalues
342  * \see qpp::hevals()
343  *
344  * \param A Eigen expression
345  * \return Eigenvalues of \a A, as a complex dynamic column vector
346  */
347 template <typename Derived>
evals(const Eigen::MatrixBase<Derived> & A)348 dyn_col_vect<cplx> evals(const Eigen::MatrixBase<Derived>& A) {
349     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
350 
351     // EXCEPTION CHECKS
352 
353     // check zero-size
354     if (!internal::check_nonzero_size(rA))
355         throw exception::ZeroSize("qpp::evals()");
356 
357     // check square matrix
358     if (!internal::check_square_mat(rA))
359         throw exception::MatrixNotSquare("qpp::evals()");
360     // END EXCEPTION CHECKS
361 
362     return eig(rA).first;
363 }
364 
365 /**
366  * \brief Eigenvectors
367  * \see qpp::hevects()
368  *
369  * \param A Eigen expression
370  * \return Eigenvectors of \a A, as columns of a complex dynamic matrix
371  */
372 template <typename Derived>
evects(const Eigen::MatrixBase<Derived> & A)373 cmat evects(const Eigen::MatrixBase<Derived>& A) {
374     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
375 
376     // EXCEPTION CHECKS
377 
378     // check zero-size
379     if (!internal::check_nonzero_size(rA))
380         throw exception::ZeroSize("qpp::evects()");
381 
382     // check square matrix
383     if (!internal::check_square_mat(rA))
384         throw exception::MatrixNotSquare("qpp::evects()");
385     // END EXCEPTION CHECKS
386 
387     Eigen::ComplexEigenSolver<cmat> es(rA.template cast<cplx>());
388 
389     return eig(rA).second;
390 }
391 
392 /**
393  * \brief Full eigen decomposition of Hermitian expression
394  * \see qpp::eig()
395  *
396  * \param A Eigen expression, assumed to be Hermitian
397  * \return Pair of:  1. Eigenvalues of \a A, as a real dynamic column vector,
398  * and 2. Eigenvectors of \a A, as columns of a complex dynamic matrix
399  */
400 template <typename Derived>
401 std::pair<dyn_col_vect<double>, cmat>
heig(const Eigen::MatrixBase<Derived> & A)402 heig(const Eigen::MatrixBase<Derived>& A) {
403     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
404 
405     // EXCEPTION CHECKS
406 
407     // check zero-size
408     if (!internal::check_nonzero_size(rA))
409         throw exception::ZeroSize("qpp::heig()");
410 
411     // check square matrix
412     if (!internal::check_square_mat(rA))
413         throw exception::MatrixNotSquare("qpp::heig()");
414     // END EXCEPTION CHECKS
415 
416     Eigen::SelfAdjointEigenSolver<cmat> es(rA.template cast<cplx>());
417 
418     return std::make_pair(es.eigenvalues(), es.eigenvectors());
419 }
420 
421 /**
422  * \brief Hermitian eigenvalues
423  * \see qpp::evals()
424  *
425  * \param A Eigen expression, assumed to be Hermitian
426  * \return Eigenvalues of Hermitian \a A, as a real dynamic column vector
427  */
428 template <typename Derived>
hevals(const Eigen::MatrixBase<Derived> & A)429 dyn_col_vect<double> hevals(const Eigen::MatrixBase<Derived>& A) {
430     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
431 
432     // EXCEPTION CHECKS
433 
434     // check zero-size
435     if (!internal::check_nonzero_size(rA))
436         throw exception::ZeroSize("qpp::hevals()");
437 
438     // check square matrix
439     if (!internal::check_square_mat(rA))
440         throw exception::MatrixNotSquare("qpp::hevals()");
441     // END EXCEPTION CHECKS
442 
443     return heig(rA).first;
444 }
445 
446 /**
447  * \brief Eigenvectors of Hermitian matrix
448  * \see qpp::evects()
449  *
450  * \param A Eigen expression, assumed to be Hermitian
451  * \return Eigenvectors of Hermitian matrix \a A, as columns of a complex matrix
452  */
453 template <typename Derived>
hevects(const Eigen::MatrixBase<Derived> & A)454 cmat hevects(const Eigen::MatrixBase<Derived>& A) {
455     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
456 
457     // EXCEPTION CHECKS
458 
459     // check zero-size
460     if (!internal::check_nonzero_size(rA))
461         throw exception::ZeroSize("qpp::hevects()");
462 
463     // check square matrix
464     if (!internal::check_square_mat(rA))
465         throw exception::MatrixNotSquare("qpp::hevects()");
466     // END EXCEPTION CHECKS
467 
468     return heig(rA).second;
469 }
470 
471 /**
472  * \brief Full singular value decomposition
473  *
474  * \param A Eigen expression
475  * \return Tuple of: 1. Left sigular vectors of \a A, as columns of a complex
476  * dynamic matrix, 2. Singular values of \a A, ordered in decreasing order,
477  * as a real dynamic column vector, and 3. Right singular vectors of \a A,
478  * as columns of a complex dynamic matrix
479  */
480 template <typename Derived>
481 std::tuple<cmat, dyn_col_vect<double>, cmat>
svd(const Eigen::MatrixBase<Derived> & A)482 svd(const Eigen::MatrixBase<Derived>& A) {
483     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
484 
485     // EXCEPTION CHECKS
486 
487     // check zero-size
488     if (!internal::check_nonzero_size(rA))
489         throw exception::ZeroSize("qpp::svd()");
490     // END EXCEPTION CHECKS
491 
492     auto const sv = rA.bdcSvd(Eigen::ComputeFullU | Eigen::ComputeFullV);
493 
494     return std::make_tuple(sv.matrixU(), sv.singularValues(), sv.matrixV());
495 }
496 
497 /**
498  * \brief Singular values
499  *
500  * \param A Eigen expression
501  * \return Singular values of \a A, ordered in decreasing order, as a real
502  * dynamic column vector
503  */
504 template <typename Derived>
svals(const Eigen::MatrixBase<Derived> & A)505 dyn_col_vect<double> svals(const Eigen::MatrixBase<Derived>& A) {
506     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
507 
508     // EXCEPTION CHECKS
509 
510     // check zero-size
511     if (!internal::check_nonzero_size(rA))
512         throw exception::ZeroSize("qpp::svals()");
513     // END EXCEPTION CHECKS
514 
515     return rA.bdcSvd().singularValues();
516 }
517 
518 /**
519  * \brief Left singular vectors
520  *
521  * \param A Eigen expression
522  * \return Complex dynamic matrix, whose columns are the left singular vectors
523  * of \a A
524  */
525 template <typename Derived>
svdU(const Eigen::MatrixBase<Derived> & A)526 cmat svdU(const Eigen::MatrixBase<Derived>& A) {
527     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
528 
529     // EXCEPTION CHECKS
530 
531     // check zero-size
532     if (!internal::check_nonzero_size(rA))
533         throw exception::ZeroSize("qpp::svdU()");
534     // END EXCEPTION CHECKS
535 
536     return rA.bdcSvd(Eigen::ComputeFullU).matrixU();
537 }
538 
539 /**
540  * \brief Right singular vectors
541  *
542  * \param A Eigen expression
543  * \return Complex dynamic matrix, whose columns are the right singular vectors
544  * of \a A
545  */
546 template <typename Derived>
svdV(const Eigen::MatrixBase<Derived> & A)547 cmat svdV(const Eigen::MatrixBase<Derived>& A) {
548     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
549 
550     // EXCEPTION CHECKS
551 
552     // check zero-size
553     if (!internal::check_nonzero_size(rA))
554         throw exception::ZeroSize("qpp::svdV()");
555     // END EXCEPTION CHECKS
556 
557     return rA.bdcSvd(Eigen::ComputeFullV).matrixV();
558 }
559 
560 // Matrix functional calculus
561 
562 /**
563  * \brief Functional calculus f(A)
564  *
565  * \note Does not take into account issues such as numerical stability etc.
566  *
567  * \param A Diagonalizable Eigen expression
568  * \param f Pointer-to-function from complex to complex
569  * \return \a \f$f(A)\f$
570  */
571 template <typename Derived>
funm(const Eigen::MatrixBase<Derived> & A,cplx (* f)(const cplx &))572 cmat funm(const Eigen::MatrixBase<Derived>& A, cplx (*f)(const cplx&)) {
573     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
574 
575     // EXCEPTION CHECKS
576 
577     // check zero-size
578     if (!internal::check_nonzero_size(rA))
579         throw exception::ZeroSize("qpp::funm()");
580 
581     // check square matrix
582     if (!internal::check_square_mat(rA))
583         throw exception::MatrixNotSquare("qpp::funm()");
584     // END EXCEPTION CHECKS
585 
586     Eigen::ComplexEigenSolver<cmat> es(rA.template cast<cplx>());
587     const cmat& evects = es.eigenvectors();
588     cmat evals = es.eigenvalues();
589     for (idx i = 0; i < static_cast<idx>(evals.rows()); ++i)
590         evals(i) = (*f)(evals(i)); // apply f(x) to each eigenvalue
591 
592     cmat evalsdiag = evals.asDiagonal();
593 
594     return evects * evalsdiag * evects.inverse();
595 }
596 
597 /**
598  * \brief Matrix square root
599  *
600  * \param A Eigen expression
601  * \return Matrix square root of \a A
602  */
603 template <typename Derived>
sqrtm(const Eigen::MatrixBase<Derived> & A)604 cmat sqrtm(const Eigen::MatrixBase<Derived>& A) {
605     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
606 
607     // EXCEPTION CHECKS
608 
609     // check zero-size
610     if (!internal::check_nonzero_size(rA))
611         throw exception::ZeroSize("qpp::sqrtm()");
612 
613     // check square matrix
614     if (!internal::check_square_mat(rA))
615         throw exception::MatrixNotSquare("qpp::sqrtm()");
616     // END EXCEPTION CHECKS
617 
618     return funm(rA, &std::sqrt);
619 }
620 
621 /**
622  * \brief Matrix absolute value
623  *
624  * \param A Eigen expression
625  * \return Matrix absolute value of \a A
626  */
627 template <typename Derived>
absm(const Eigen::MatrixBase<Derived> & A)628 cmat absm(const Eigen::MatrixBase<Derived>& A) {
629     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
630 
631     // EXCEPTION CHECKS
632 
633     // check zero-size
634     if (!internal::check_nonzero_size(rA))
635         throw exception::ZeroSize("qpp::absm()");
636 
637     // check square matrix
638     if (!internal::check_square_mat(rA))
639         throw exception::MatrixNotSquare("qpp::absm()");
640     // END EXCEPTION CHECKS
641 
642     return sqrtm(adjoint(rA) * rA);
643 }
644 
645 /**
646  * \brief Matrix exponential
647  *
648  * \param A Eigen expression
649  * \return Matrix exponential of \a A
650  */
651 template <typename Derived>
expm(const Eigen::MatrixBase<Derived> & A)652 cmat expm(const Eigen::MatrixBase<Derived>& A) {
653     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
654 
655     // EXCEPTION CHECKS
656 
657     // check zero-size
658     if (!internal::check_nonzero_size(rA))
659         throw exception::ZeroSize("qpp::expm()");
660 
661     // check square matrix
662     if (!internal::check_square_mat(rA))
663         throw exception::MatrixNotSquare("qpp::expm()");
664     // END EXCEPTION CHECKS
665 
666     return funm(rA, &std::exp);
667 }
668 
669 /**
670  * \brief Matrix logarithm
671  *
672  * \param A Eigen expression
673  * \return Matrix logarithm of \a A
674  */
675 template <typename Derived>
logm(const Eigen::MatrixBase<Derived> & A)676 cmat logm(const Eigen::MatrixBase<Derived>& A) {
677     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
678 
679     // EXCEPTION CHECKS
680 
681     // check zero-size
682     if (!internal::check_nonzero_size(rA))
683         throw exception::ZeroSize("qpp::logm()");
684 
685     // check square matrix
686     if (!internal::check_square_mat(rA))
687         throw exception::MatrixNotSquare("qpp::logm()");
688     // END EXCEPTION CHECKS
689 
690     return funm(rA, &std::log);
691 }
692 
693 /**
694  * \brief Matrix sin
695  *
696  * \param A Eigen expression
697  * \return Matrix sine of \a A
698  */
699 template <typename Derived>
sinm(const Eigen::MatrixBase<Derived> & A)700 cmat sinm(const Eigen::MatrixBase<Derived>& A) {
701     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
702 
703     // EXCEPTION CHECKS
704 
705     // check zero-size
706     if (!internal::check_nonzero_size(rA))
707         throw exception::ZeroSize("qpp::sinm()");
708 
709     // check square matrix
710     if (!internal::check_square_mat(rA))
711         throw exception::MatrixNotSquare("qpp::sinm()");
712     // END EXCEPTION CHECKS
713 
714     return funm(rA, &std::sin);
715 }
716 
717 /**
718  * \brief Matrix cos
719  *
720  * \param A Eigen expression
721  * \return Matrix cosine of \a A
722  */
723 template <typename Derived>
cosm(const Eigen::MatrixBase<Derived> & A)724 cmat cosm(const Eigen::MatrixBase<Derived>& A) {
725     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
726 
727     // EXCEPTION CHECKS
728 
729     // check zero-size
730     if (!internal::check_nonzero_size(rA))
731         throw exception::ZeroSize("qpp::cosm()");
732 
733     // check square matrix
734     if (!internal::check_square_mat(rA))
735         throw exception::MatrixNotSquare("qpp::cosm()");
736     // END EXCEPTION CHECKS
737 
738     return funm(rA, &std::cos);
739 }
740 
741 /**
742  * \brief Matrix power
743  * \see qpp::powm()
744  *
745  * Uses the spectral decomposition of \a A to compute the matrix power. By
746  * convention \f$A^0 = I\f$.
747  *
748  * \param A Diagonalizable Eigen expression
749  * \param z Complex number
750  * \return Matrix power \f$A^z\f$
751  */
752 template <typename Derived>
spectralpowm(const Eigen::MatrixBase<Derived> & A,const cplx z)753 cmat spectralpowm(const Eigen::MatrixBase<Derived>& A, const cplx z) {
754     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
755 
756     // EXCEPTION CHECKS
757 
758     // check zero-size
759     if (!internal::check_nonzero_size(rA))
760         throw exception::ZeroSize("qpp::spectralpowm()");
761 
762     // check square matrix
763     if (!internal::check_square_mat(rA))
764         throw exception::MatrixNotSquare("qpp::spectralpowm()");
765     // END EXCEPTION CHECKS
766 
767     // Define A^0 = Id, for z IDENTICALLY zero
768     if (real(z) == 0 && imag(z) == 0)
769         return cmat::Identity(rA.rows(), rA.cols());
770 
771     Eigen::ComplexEigenSolver<cmat> es(rA.template cast<cplx>());
772     cmat evects = es.eigenvectors();
773     cmat evals = es.eigenvalues();
774     for (idx i = 0; i < static_cast<idx>(evals.rows()); ++i)
775         evals(i) = std::pow(evals(i), z);
776 
777     cmat evalsdiag = evals.asDiagonal();
778 
779     return evects * evalsdiag * evects.inverse();
780 }
781 
782 /**
783  * \brief Fast matrix power based on the SQUARE-AND-MULTIPLY algorithm
784  * \see qpp::spectralpowm()
785  *
786  * Explicitly multiplies the matrix \a A with itself \a n times. By convention
787  * \f$A^0 = I\f$.
788  *
789  * \param A Eigen expression
790  * \param n Non-negative integer
791  * \return Matrix power \f$A^n\f$, as a dynamic matrix over the same scalar
792  * field as \a A
793  */
794 template <typename Derived>
powm(const Eigen::MatrixBase<Derived> & A,idx n)795 dyn_mat<typename Derived::Scalar> powm(const Eigen::MatrixBase<Derived>& A,
796                                        idx n) {
797     // EXCEPTION CHECKS
798 
799     // check zero-size
800     if (!internal::check_nonzero_size(A))
801         throw exception::ZeroSize("qpp::powm()");
802 
803     // check square matrix
804     if (!internal::check_square_mat(A))
805         throw exception::MatrixNotSquare("qpp::powm()");
806     // END EXCEPTION CHECKS
807 
808     // if n = 1, return the matrix unchanged
809     if (n == 1)
810         return A;
811 
812     dyn_mat<typename Derived::Scalar> result =
813         dyn_mat<typename Derived::Scalar>::Identity(A.rows(), A.cols());
814 
815     // if n = 0, return the identity (as just prepared in result)
816     if (n == 0)
817         return result;
818 
819     dyn_mat<typename Derived::Scalar> cA = A.derived(); // copy
820 
821     // fast matrix power
822     for (; n > 0; n /= 2) {
823         if (n % 2)
824             result = (result * cA).eval();
825         cA = (cA * cA).eval();
826     }
827 
828     return result;
829 }
830 
831 /**
832  * \brief Schatten matrix norm
833  *
834  * \param A Eigen expression
835  * \param p Real number, greater or equal to 1, use qpp::infty for
836  * \f$p = \infty\f$
837  * \return Schatten-\a p matrix norm of \a A
838  */
839 template <typename Derived>
schatten(const Eigen::MatrixBase<Derived> & A,double p)840 double schatten(const Eigen::MatrixBase<Derived>& A, double p) {
841     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
842 
843     // EXCEPTION CHECKS
844 
845     // check zero-size
846     if (!internal::check_nonzero_size(rA))
847         throw exception::ZeroSize("qpp::schatten()");
848     if (p < 1)
849         throw exception::OutOfRange("qpp::schatten()");
850     // END EXCEPTION CHECKS
851 
852     if (p == infty) // infinity norm (largest singular value)
853         return svals(rA)(0);
854 
855     const dyn_col_vect<double> sv = svals(rA);
856     double result = 0;
857     for (idx i = 0; i < static_cast<idx>(sv.rows()); ++i)
858         result += std::pow(sv[i], p);
859 
860     return std::pow(result, 1. / p);
861 }
862 
863 // other functions
864 
865 /**
866  * \brief Functor
867  *
868  * \param A Eigen expression
869  * \param f Pointer-to-function from scalars of \a A to \a OutputScalar
870  * \return Component-wise \f$f(A)\f$, as a dynamic matrix over the
871  * \a OutputScalar scalar field
872  */
873 template <typename OutputScalar, typename Derived>
cwise(const Eigen::MatrixBase<Derived> & A,OutputScalar (* f)(typename Derived::Scalar))874 dyn_mat<OutputScalar> cwise(const Eigen::MatrixBase<Derived>& A,
875                             OutputScalar (*f)(typename Derived::Scalar)) {
876     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
877 
878     // EXCEPTION CHECKS
879 
880     // check zero-size
881     if (!internal::check_nonzero_size(rA))
882         throw exception::ZeroSize("qpp::cwise()");
883     // END EXCEPTION CHECKS
884 
885     dyn_mat<OutputScalar> result(rA.rows(), rA.cols());
886 
887 #ifdef HAS_OPENMP
888 // NOLINTNEXTLINE
889 #pragma omp parallel for collapse(2)
890 #endif // HAS_OPENMP
891     // column major order for speed
892     for (idx j = 0; j < static_cast<idx>(rA.cols()); ++j)
893         for (idx i = 0; i < static_cast<idx>(rA.rows()); ++i)
894             result(i, j) = (*f)(rA(i, j));
895 
896     return result;
897 }
898 
899 // Kronecker product of multiple matrices, preserve return type
900 // variadic template
901 /**
902  * \brief Kronecker product
903  * \see qpp::kronpow()
904  *
905  * Used to stop the recursion for the variadic template version of qpp::kron()
906  *
907  * \param head Eigen expression
908  * \return Its argument \a head
909  */
910 template <typename T>
kron(const T & head)911 dyn_mat<typename T::Scalar> kron(const T& head) {
912     return head;
913 }
914 
915 /**
916  * \brief Kronecker product
917  * \see qpp::kronpow()
918  *
919  * \param head Eigen expression
920  * \param tail Variadic Eigen expression (zero or more parameters)
921  * \return Kronecker product of all input parameters, evaluated from left to
922  * right, as a dynamic matrix over the same scalar field as its arguments
923  */
924 template <typename T, typename... Args>
kron(const T & head,const Args &...tail)925 dyn_mat<typename T::Scalar> kron(const T& head, const Args&... tail) {
926     return internal::kron2(head, kron(tail...));
927 }
928 
929 /**
930  * \brief Kronecker product
931  * \see qpp::kronpow()
932  *
933  * \param As std::vector of Eigen expressions
934  * \return Kronecker product of all elements in \a As, evaluated from left to
935  * right, as a dynamic matrix over the same scalar field as its arguments
936  */
937 template <typename Derived>
kron(const std::vector<Derived> & As)938 dyn_mat<typename Derived::Scalar> kron(const std::vector<Derived>& As) {
939     // EXCEPTION CHECKS
940 
941     if (As.empty())
942         throw exception::ZeroSize("qpp::kron()");
943 
944     for (auto&& elem : As)
945         if (!internal::check_nonzero_size(elem))
946             throw exception::ZeroSize("qpp::kron()");
947     // END EXCEPTION CHECKS
948 
949     dyn_mat<typename Derived::Scalar> result = As[0].derived();
950     for (idx i = 1; i < As.size(); ++i) {
951         result = kron(result, As[i]);
952     }
953 
954     return result;
955 }
956 
957 // Kronecker product of a list of matrices, preserve return type
958 // deduce the template parameters from initializer_list
959 /**
960  * \brief Kronecker product
961  * \see qpp::kronpow()
962  *
963  * \param As std::initializer_list of Eigen expressions, such as
964  * \a {A1, A2, ... ,Ak}
965  * \return Kronecker product of all elements in \a As, evaluated from left to
966  * right, as a dynamic matrix over the same scalar field as its arguments
967  */
968 template <typename Derived>
969 dyn_mat<typename Derived::Scalar>
kron(const std::initializer_list<Derived> & As)970 kron(const std::initializer_list<Derived>& As) {
971     return kron(std::vector<Derived>(As));
972 }
973 
974 /**
975  * \brief Kronecker power
976  * \see qpp::kron()
977  *
978  * \param A Eigen expression
979  * \param n Positive integer
980  * \return Kronecker product of \a A with itself \a n times \f$A^{\otimes n}\f$,
981  * as a dynamic matrix over the same scalar field as \a A
982  */
983 template <typename Derived>
kronpow(const Eigen::MatrixBase<Derived> & A,idx n)984 dyn_mat<typename Derived::Scalar> kronpow(const Eigen::MatrixBase<Derived>& A,
985                                           idx n) {
986     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
987 
988     // EXCEPTION CHECKS
989 
990     // check zero-size
991     if (!internal::check_nonzero_size(rA))
992         throw exception::ZeroSize("qpp::kronpow()");
993 
994     // check out of range
995     if (n == 0)
996         throw exception::OutOfRange("qpp::kronpow()");
997     // END EXCEPTION CHECKS
998 
999     std::vector<dyn_mat<typename Derived::Scalar>> As(n, rA);
1000 
1001     return kron(As);
1002 }
1003 
1004 // Direct sum of multiple matrices, preserve return type
1005 // variadic template
1006 /**
1007  * \brief Direct sum
1008  * \see qpp::dirsumpow()
1009  *
1010  * Used to stop the recursion for the variadic template version of qpp::dirsum()
1011  *
1012  * \param head Eigen expression
1013  * \return Its argument \a head
1014  */
1015 template <typename T>
dirsum(const T & head)1016 dyn_mat<typename T::Scalar> dirsum(const T& head) {
1017     return head;
1018 }
1019 
1020 /**
1021  * \brief Direct sum
1022  * \see qpp::dirsumpow()
1023  *
1024  * \param head Eigen expression
1025  * \param tail Variadic Eigen expression (zero or more parameters)
1026  * \return Direct sum of all input parameters, evaluated from left to right, as
1027  * a dynamic matrix over the same scalar field as its arguments
1028  */
1029 template <typename T, typename... Args>
dirsum(const T & head,const Args &...tail)1030 dyn_mat<typename T::Scalar> dirsum(const T& head, const Args&... tail) {
1031     return internal::dirsum2(head, dirsum(tail...));
1032 }
1033 
1034 /**
1035  * \brief Direct sum
1036  * \see qpp::dirsumpow()
1037  *
1038  * \param As std::vector of Eigen expressions
1039  * \return Direct sum of all elements in \a As, evaluated from left to right, as
1040  * a dynamic matrix over the same scalar field as its arguments
1041  */
1042 template <typename Derived>
dirsum(const std::vector<Derived> & As)1043 dyn_mat<typename Derived::Scalar> dirsum(const std::vector<Derived>& As) {
1044     // EXCEPTION CHECKS
1045 
1046     if (As.empty())
1047         throw exception::ZeroSize("qpp::dirsum()");
1048 
1049     for (auto&& elem : As)
1050         if (!internal::check_nonzero_size(elem))
1051             throw exception::ZeroSize("qpp::dirsum()");
1052     // END EXCEPTION CHECKS
1053 
1054     idx total_rows = 0, total_cols = 0;
1055     for (idx i = 0; i < As.size(); ++i) {
1056         total_rows += static_cast<idx>(As[i].rows());
1057         total_cols += static_cast<idx>(As[i].cols());
1058     }
1059     dyn_mat<typename Derived::Scalar> result =
1060         dyn_mat<typename Derived::Scalar>::Zero(total_rows, total_cols);
1061 
1062     idx cur_row = 0, cur_col = 0;
1063     for (idx i = 0; i < As.size(); ++i) {
1064         result.block(cur_row, cur_col, As[i].rows(), As[i].cols()) = As[i];
1065         cur_row += static_cast<idx>(As[i].rows());
1066         cur_col += static_cast<idx>(As[i].cols());
1067     }
1068 
1069     return result;
1070 }
1071 
1072 // Direct sum of a list of matrices, preserve return type
1073 // deduce the template parameters from initializer_list
1074 /**
1075  * \brief Direct sum
1076  * \see qpp::dirsumpow()
1077  *
1078  * \param As std::initializer_list of Eigen expressions,
1079  * such as \a {A1, A2, ... ,Ak}
1080  * \return Direct sum of all elements in \a As, evaluated from left to right, as
1081  * a dynamic matrix over the same scalar field as its arguments
1082  */
1083 template <typename Derived>
1084 dyn_mat<typename Derived::Scalar>
dirsum(const std::initializer_list<Derived> & As)1085 dirsum(const std::initializer_list<Derived>& As) {
1086     return dirsum(std::vector<Derived>(As));
1087 }
1088 
1089 /**
1090  * \brief Direct sum power
1091  * \see qpp::dirsum()
1092  *
1093  * \param A Eigen expression
1094  * \param n Positive integer
1095  * \return Direct sum of \a A with itself \a n times \f$A^{\oplus n}\f$, as a
1096  * dynamic matrix over the same scalar field as \a A
1097  */
1098 template <typename Derived>
dirsumpow(const Eigen::MatrixBase<Derived> & A,idx n)1099 dyn_mat<typename Derived::Scalar> dirsumpow(const Eigen::MatrixBase<Derived>& A,
1100                                             idx n) {
1101     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
1102 
1103     // EXCEPTION CHECKS
1104 
1105     // check zero-size
1106     if (!internal::check_nonzero_size(rA))
1107         throw exception::ZeroSize("qpp::dirsumpow()");
1108 
1109     // check out of range
1110     if (n == 0)
1111         throw exception::OutOfRange("qpp::dirsumpow()");
1112     // END EXCEPTION CHECKS
1113 
1114     std::vector<dyn_mat<typename Derived::Scalar>> As(n, rA);
1115 
1116     return dirsum(As);
1117 }
1118 
1119 /**
1120  * \brief Reshape
1121  *
1122  * Uses column-major order when reshaping (same as MATLAB)
1123  *
1124  * \param A Eigen expression
1125  * \param rows Number of rows of the reshaped matrix
1126  * \param cols Number of columns of the reshaped matrix
1127  * \return Reshaped matrix with \a rows rows and \a cols columns, as a dynamic
1128  * matrix over the same scalar field as \a A
1129  */
1130 template <typename Derived>
reshape(const Eigen::MatrixBase<Derived> & A,idx rows,idx cols)1131 dyn_mat<typename Derived::Scalar> reshape(const Eigen::MatrixBase<Derived>& A,
1132                                           idx rows, idx cols) {
1133     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
1134 
1135     idx Arows = static_cast<idx>(rA.rows());
1136     idx Acols = static_cast<idx>(rA.cols());
1137 
1138     // EXCEPTION CHECKS
1139 
1140     // check zero-size
1141     if (!internal::check_nonzero_size(rA))
1142         throw exception::ZeroSize("qpp::reshape()");
1143 
1144     if (Arows * Acols != rows * cols)
1145         throw exception::DimsMismatchMatrix("qpp::reshape()");
1146     // END EXCEPTION CHECKS
1147 
1148     return Eigen::Map<dyn_mat<typename Derived::Scalar>>(
1149         const_cast<typename Derived::Scalar*>(rA.data()), rows, cols);
1150 }
1151 
1152 /**
1153  * \brief Commutator
1154  * \see qpp::anticomm()
1155  *
1156  * Commutator \f$[A,B] = AB - BA\f$. Both \a A and \a B must be Eigen
1157  * expressions over the same scalar field.
1158  *
1159  * \param A Eigen expression
1160  * \param B Eigen expression
1161  * \return Commutator \f$AB -BA\f$, as a dynamic matrix over the same scalar
1162  * field as \a A
1163  */
1164 template <typename Derived1, typename Derived2>
comm(const Eigen::MatrixBase<Derived1> & A,const Eigen::MatrixBase<Derived2> & B)1165 dyn_mat<typename Derived1::Scalar> comm(const Eigen::MatrixBase<Derived1>& A,
1166                                         const Eigen::MatrixBase<Derived2>& B) {
1167     const dyn_mat<typename Derived1::Scalar>& rA = A.derived();
1168     const dyn_mat<typename Derived2::Scalar>& rB = B.derived();
1169 
1170     // EXCEPTION CHECKS
1171 
1172     // check types
1173     if (!std::is_same<typename Derived1::Scalar,
1174                       typename Derived2::Scalar>::value)
1175         throw exception::TypeMismatch("qpp::comm()");
1176 
1177     // check zero-size
1178     if (!internal::check_nonzero_size(rA) || !internal::check_nonzero_size(rB))
1179         throw exception::ZeroSize("qpp::comm()");
1180 
1181     // check square matrices
1182     if (!internal::check_square_mat(rA) || !internal::check_square_mat(rB))
1183         throw exception::MatrixNotSquare("qpp::comm()");
1184 
1185     // check equal dimensions
1186     if (rA.rows() != rB.rows())
1187         throw exception::DimsNotEqual("qpp::comm()");
1188     // END EXCEPTION CHECKS
1189 
1190     return rA * rB - rB * rA;
1191 }
1192 
1193 /**
1194  * \brief Anti-commutator
1195  * \see qpp::comm()
1196  *
1197  * Anti-commutator \f$\{A,B\} = AB + BA\f$.
1198  * Both \a A and \a B must be Eigen expressions over the same scalar field.
1199  *
1200  * \param A Eigen expression
1201  * \param B Eigen expression
1202  * \return Anti-commutator \f$AB +BA\f$, as a dynamic matrix over the same
1203  * scalar field as \a A
1204  */
1205 template <typename Derived1, typename Derived2>
1206 dyn_mat<typename Derived1::Scalar>
anticomm(const Eigen::MatrixBase<Derived1> & A,const Eigen::MatrixBase<Derived2> & B)1207 anticomm(const Eigen::MatrixBase<Derived1>& A,
1208          const Eigen::MatrixBase<Derived2>& B) {
1209     const dyn_mat<typename Derived1::Scalar>& rA = A.derived();
1210     const dyn_mat<typename Derived2::Scalar>& rB = B.derived();
1211 
1212     // EXCEPTION CHECKS
1213 
1214     // check types
1215     if (!std::is_same<typename Derived1::Scalar,
1216                       typename Derived2::Scalar>::value)
1217         throw exception::TypeMismatch("qpp::anticomm()");
1218 
1219     // check zero-size
1220     if (!internal::check_nonzero_size(rA) || !internal::check_nonzero_size(rB))
1221         throw exception::ZeroSize("qpp::anticomm()");
1222 
1223     // check square matrices
1224     if (!internal::check_square_mat(rA) || !internal::check_square_mat(rB))
1225         throw exception::MatrixNotSquare("qpp::anticomm()");
1226 
1227     // check equal dimensions
1228     if (rA.rows() != rB.rows())
1229         throw exception::DimsNotEqual("qpp::anticomm()");
1230     // END EXCEPTION CHECKS
1231 
1232     return rA * rB + rB * rA;
1233 }
1234 
1235 /**
1236  * \brief Projector
1237  *
1238  * Normalized projector onto state vector
1239  *
1240  * \param A Eigen expression
1241  * \return Projector onto the state vector \a A, or the matrix \a Zero if \a A
1242  * has norm zero, as a dynamic matrix over the same scalar field as \a A
1243  */
1244 template <typename Derived>
prj(const Eigen::MatrixBase<Derived> & A)1245 dyn_mat<typename Derived::Scalar> prj(const Eigen::MatrixBase<Derived>& A) {
1246     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
1247 
1248     // EXCEPTION CHECKS
1249 
1250     // check zero-size
1251     if (!internal::check_nonzero_size(rA))
1252         throw exception::ZeroSize("qpp::prj()");
1253 
1254     // check column vector
1255     if (!internal::check_cvector(rA))
1256         throw exception::MatrixNotCvector("qpp::prj()");
1257     // END EXCEPTION CHECKS
1258 
1259     double normA = norm(rA);
1260     if (normA > 0)
1261         return rA * adjoint(rA) / (normA * normA);
1262     else
1263         return dyn_mat<typename Derived::Scalar>::Zero(rA.rows(), rA.rows());
1264 }
1265 
1266 /**
1267  * \brief Gram-Schmidt orthogonalization
1268  *
1269  * \param As std::vector of Eigen expressions as column vectors
1270  * \return Gram-Schmidt vectors of \a As as columns of a dynamic matrix over the
1271  * same scalar field as its arguments
1272  */
1273 template <typename Derived>
grams(const std::vector<Derived> & As)1274 dyn_mat<typename Derived::Scalar> grams(const std::vector<Derived>& As) {
1275     // EXCEPTION CHECKS
1276 
1277     // check empty list
1278     if (!internal::check_nonzero_size(As))
1279         throw exception::ZeroSize("qpp::grams()");
1280 
1281     for (auto&& elem : As)
1282         if (!internal::check_nonzero_size(elem))
1283             throw exception::ZeroSize("qpp::grams()");
1284 
1285     // check that As[0] is a column vector
1286     if (!internal::check_cvector(As[0]))
1287         throw exception::MatrixNotCvector("qpp::grams()");
1288 
1289     // now check that all the rest match the size of the first vector
1290     for (auto&& elem : As)
1291         if (elem.rows() != As[0].rows() || elem.cols() != 1)
1292             throw exception::DimsNotEqual("qpp::grams()");
1293     // END EXCEPTION CHECKS
1294 
1295     dyn_mat<typename Derived::Scalar> cut =
1296         dyn_mat<typename Derived::Scalar>::Identity(As[0].rows(), As[0].rows());
1297 
1298     dyn_mat<typename Derived::Scalar> vi =
1299         dyn_mat<typename Derived::Scalar>::Zero(As[0].rows(), 1);
1300 
1301     std::vector<dyn_mat<typename Derived::Scalar>> outvecs;
1302     // find the first non-zero vector in the list
1303     idx pos = 0;
1304     for (pos = 0; pos < As.size(); ++pos) {
1305         if (norm(As[pos]) > 0) // add it as the first element
1306         {
1307             outvecs.emplace_back(As[pos]);
1308             break;
1309         }
1310     }
1311 
1312     // start the process
1313     for (idx i = pos + 1; i < As.size(); ++i) {
1314         cut -= prj(outvecs[i - 1 - pos]);
1315         vi = cut * As[i];
1316         outvecs.emplace_back(vi);
1317     }
1318 
1319     dyn_mat<typename Derived::Scalar> result(As[0].rows(), outvecs.size());
1320 
1321     idx tmp = 0;
1322     for (auto&& elem : outvecs) {
1323         double normA = norm(elem);
1324         if (normA > 0) // we add only the non-zero vectors
1325         {
1326             result.col(tmp++) = elem / normA;
1327         }
1328     }
1329 
1330     return result.block(0, 0, As[0].rows(), tmp);
1331 }
1332 
1333 // deduce the template parameters from initializer_list
1334 /**
1335  * \brief Gram-Schmidt orthogonalization
1336  *
1337  * \param As std::initializer_list of Eigen expressions as column vectors
1338  * \return Gram-Schmidt vectors of \a As as columns of a dynamic matrix over the
1339  * same scalar field as its arguments
1340  */
1341 template <typename Derived>
1342 dyn_mat<typename Derived::Scalar>
grams(const std::initializer_list<Derived> & As)1343 grams(const std::initializer_list<Derived>& As) {
1344     return grams(std::vector<Derived>(As));
1345 }
1346 
1347 /**
1348  * \brief Gram-Schmidt orthogonalization
1349  *
1350  * \param A Eigen expression, the input vectors are the columns of \a A
1351  * \return Gram-Schmidt vectors of the columns of \a A, as columns of a dynamic
1352  * matrix over the same scalar field as \a A
1353  */
1354 template <typename Derived>
grams(const Eigen::MatrixBase<Derived> & A)1355 dyn_mat<typename Derived::Scalar> grams(const Eigen::MatrixBase<Derived>& A) {
1356     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
1357 
1358     // EXCEPTION CHECKS
1359 
1360     if (!internal::check_nonzero_size(rA))
1361         throw exception::ZeroSize("qpp::grams()");
1362     // END EXCEPTION CHECKS
1363 
1364     std::vector<dyn_mat<typename Derived::Scalar>> input;
1365 
1366     for (idx i = 0; i < static_cast<idx>(rA.cols()); ++i)
1367         input.emplace_back(rA.col(i));
1368 
1369     return grams<dyn_mat<typename Derived::Scalar>>(input);
1370 }
1371 
1372 /**
1373  * \brief Non-negative integer index to multi-index
1374  * \see qpp::multiidx2n()
1375  *
1376  * Uses standard lexicographical order, i.e., 00...0, 00...1 etc.
1377  *
1378  * \param n Non-negative integer index
1379  * \param dims Dimensions of the multi-partite system
1380  * \return Multi-index of the same size as \a dims
1381  */
n2multiidx(idx n,const std::vector<idx> & dims)1382 inline std::vector<idx> n2multiidx(idx n, const std::vector<idx>& dims) {
1383     // EXCEPTION CHECKS
1384 
1385     if (dims.size() > internal::maxn)
1386         throw exception::OutOfRange("qpp::n2multiidx()");
1387 
1388     if (!internal::check_dims(dims)) {
1389         throw exception::DimsInvalid("qpp::n2multiidx()");
1390     }
1391 
1392     if (n >= std::accumulate(std::begin(dims), std::end(dims),
1393                              static_cast<idx>(1), std::multiplies<idx>())) {
1394         throw exception::OutOfRange("qpp::n2multiidx()");
1395     }
1396     // END EXCEPTION CHECKS
1397 
1398     // double the size for matrices reshaped as vectors
1399     idx result[2 * internal::maxn];
1400     internal::n2multiidx(n, dims.size(), dims.data(), result);
1401 
1402     return std::vector<idx>(result, result + dims.size());
1403 }
1404 
1405 /**
1406  * \brief Multi-index to non-negative integer index
1407  * \see qpp::n2multiidx()
1408  *
1409  * Uses standard lexicographical order, i.e., 00...0, 00...1 etc.
1410  *
1411  * \param midx Multi-index
1412  * \param dims Dimensions of the multi-partite system
1413  * \return Non-negative integer index
1414  */
multiidx2n(const std::vector<idx> & midx,const std::vector<idx> & dims)1415 inline idx multiidx2n(const std::vector<idx>& midx,
1416                       const std::vector<idx>& dims) {
1417     // EXCEPTION CHECKS
1418     if (midx.size() != dims.size())
1419         throw exception::SizeMismatch("qpp::multiidx2n()");
1420 
1421     if (!internal::check_dims(dims))
1422         throw exception::DimsInvalid("qpp::multiidx2n()");
1423 
1424     if (dims.size() > internal::maxn)
1425         throw exception::OutOfRange("qpp::multiidx2n()");
1426 
1427     for (idx i = 0; i < dims.size(); ++i)
1428         if (midx[i] >= dims[i]) {
1429             throw exception::OutOfRange("qpp::multiidx2n()");
1430         }
1431     // END EXCEPTION CHECKS
1432 
1433     return internal::multiidx2n(midx.data(), dims.size(), dims.data());
1434 }
1435 
1436 /**
1437  * \brief Multi-partite qudit ket
1438  * \see qpp::operator "" _ket()
1439  *
1440  * Constructs the multi-partite qudit ket \f$|\mathrm{mask}\rangle\f$,
1441  * where \a mask is a std::vector of non-negative integers. Each element in
1442  * \a mask has to be smaller than the corresponding element in \a dims.
1443  *
1444  * \param mask std::vector of non-negative integers
1445  * \param dims Dimensions of the multi-partite system
1446  * \return Multi-partite qudit state vector, as a complex dynamic column vector
1447  */
mket(const std::vector<idx> & mask,const std::vector<idx> & dims)1448 inline ket mket(const std::vector<idx>& mask, const std::vector<idx>& dims) {
1449     idx n = mask.size();
1450 
1451     idx D = std::accumulate(std::begin(dims), std::end(dims),
1452                             static_cast<idx>(1), std::multiplies<idx>());
1453 
1454     // EXCEPTION CHECKS
1455 
1456     // check zero size
1457     if (n == 0)
1458         throw exception::ZeroSize("qpp::mket()");
1459     // check valid dims
1460     if (!internal::check_dims(dims))
1461         throw exception::DimsInvalid("qpp::mket()");
1462     // check mask and dims have the same size
1463     if (mask.size() != dims.size())
1464         throw exception::SubsysMismatchDims("qpp::mket()");
1465     // check mask is a valid vector
1466     for (idx i = 0; i < n; ++i)
1467         if (mask[i] >= dims[i])
1468             throw exception::SubsysMismatchDims("qpp::mket()");
1469     // END EXCEPTION CHECKS
1470 
1471     ket result = ket::Zero(D);
1472     idx pos = multiidx2n(mask, dims);
1473     result(pos) = 1;
1474 
1475     return result;
1476 }
1477 
1478 /**
1479  * \brief Multi-partite qudit ket
1480  * \see qpp::operator "" _ket()
1481  *
1482  * Constructs the multi-partite qudit ket \f$|\mathrm{mask}\rangle\f$, all
1483  * subsystem having equal dimension \a d. \a mask is a std::vector of
1484  * non-negative integers, and each element in \a mask has to be strictly smaller
1485  * than \a d.
1486  *
1487  * \param mask std::vector of non-negative integers
1488  * \param d Subsystem dimensions
1489  * \return Multi-partite qudit state vector, as a complex dynamic column vector
1490  */
mket(const std::vector<idx> & mask,idx d=2)1491 inline ket mket(const std::vector<idx>& mask, idx d = 2) {
1492     idx n = mask.size();
1493     idx D = static_cast<idx>(std::llround(std::pow(d, n)));
1494 
1495     // EXCEPTION CHECKS
1496 
1497     // check zero size
1498     if (n == 0)
1499         throw exception::ZeroSize("qpp::mket()");
1500 
1501     // check valid dims
1502     if (d == 0)
1503         throw exception::DimsInvalid("qpp::mket()");
1504 
1505     // check mask is a valid vector
1506     for (idx i = 0; i < n; ++i)
1507         if (mask[i] >= d)
1508             throw exception::SubsysMismatchDims("qpp::mket()");
1509     // END EXCEPTION CHECKS
1510 
1511     ket result = ket::Zero(D);
1512     std::vector<idx> dims(n, d);
1513     idx pos = multiidx2n(mask, dims);
1514     result(pos) = 1;
1515 
1516     return result;
1517 }
1518 
1519 /**
1520  * \brief Projector onto multi-partite qudit ket
1521  * \see qpp::operator "" _prj()
1522  *
1523  * Constructs the projector onto the multi-partite qudit ket
1524  * \f$|\mathrm{mask}\rangle\f$, where \a mask is a std::vector of non-negative
1525  * integers. Each element in \a mask has to be smaller than the corresponding
1526  * element in \a dims.
1527  *
1528  * \param mask std::vector of non-negative integers
1529  * \param dims Dimensions of the multi-partite system
1530  * \return Projector onto multi-partite qudit state vector, as a complex dynamic
1531  * matrix
1532  */
mprj(const std::vector<idx> & mask,const std::vector<idx> & dims)1533 inline cmat mprj(const std::vector<idx>& mask, const std::vector<idx>& dims) {
1534     idx n = mask.size();
1535 
1536     idx D = std::accumulate(std::begin(dims), std::end(dims),
1537                             static_cast<idx>(1), std::multiplies<idx>());
1538 
1539     // EXCEPTION CHECKS
1540 
1541     // check zero size
1542     if (n == 0)
1543         throw exception::ZeroSize("qpp::mprj()");
1544     // check valid dims
1545     if (!internal::check_dims(dims))
1546         throw exception::DimsInvalid("qpp::mprj()");
1547     // check mask and dims have the same size
1548     if (mask.size() != dims.size())
1549         throw exception::SubsysMismatchDims("qpp::mprj()");
1550     // check mask is a valid vector
1551     for (idx i = 0; i < n; ++i)
1552         if (mask[i] >= dims[i])
1553             throw exception::SubsysMismatchDims("qpp::mprj()");
1554     // END EXCEPTION CHECKS
1555 
1556     cmat result = cmat::Zero(D, D);
1557     idx pos = multiidx2n(mask, dims);
1558     result(pos, pos) = 1;
1559 
1560     return result;
1561 }
1562 
1563 /**
1564  * \brief Projector onto multi-partite qudit ket
1565  * \see qpp::operator "" _prj()
1566  *
1567  * Constructs the projector onto the multi-partite qudit ket
1568  * \f$|\mathrm{mask}\rangle\f$, all subsystem having equal dimension \a d.
1569  * \a mask is a std::vector of non-negative integers, and each element in
1570  * \a mask has to be strictly smaller than \a d.
1571  *
1572  * \param mask std::vector of non-negative integers
1573  * \param d Subsystem dimensions
1574  * \return Projector onto multi-partite qudit state vector, as a complex dynamic
1575  * matrix
1576  */
mprj(const std::vector<idx> & mask,idx d=2)1577 inline cmat mprj(const std::vector<idx>& mask, idx d = 2) {
1578     idx n = mask.size();
1579     idx D = static_cast<idx>(std::llround(std::pow(d, n)));
1580 
1581     // EXCEPTION CHECKS
1582 
1583     // check zero size
1584     if (n == 0)
1585         throw exception::ZeroSize("qpp::mprj()");
1586 
1587     // check valid dims
1588     if (d == 0)
1589         throw exception::DimsInvalid("qpp::mprj()");
1590 
1591     // check mask is a valid vector
1592     for (idx i = 0; i < n; ++i)
1593         if (mask[i] >= d)
1594             throw exception::SubsysMismatchDims("qpp::mprj()");
1595     // END EXCEPTION CHECKS
1596 
1597     cmat result = cmat::Zero(D, D);
1598     std::vector<idx> dims(n, d);
1599     idx pos = multiidx2n(mask, dims);
1600     result(pos, pos) = 1;
1601 
1602     return result;
1603 }
1604 
1605 /**
1606 * \brief Computes the absolute values squared of an STL-like range of complex
1607 * numbers
1608 
1609 * \param first Iterator to the first element of the range
1610 * \param last  Iterator to the last element of the range
1611 * \return Real vector consisting of the range absolute values squared
1612 */
1613 template <typename InputIterator>
abssq(InputIterator first,InputIterator last)1614 std::vector<double> abssq(InputIterator first, InputIterator last) {
1615     std::vector<double> weights(std::distance(first, last));
1616     std::transform(first, last, std::begin(weights),
1617                    [](cplx z) -> double { return std::norm(z); });
1618 
1619     return weights;
1620 }
1621 
1622 /**
1623  * \brief Computes the absolute values squared of an STL-like container
1624  *
1625  * \param c STL-like container
1626  * \return Real vector consisting of the container's absolute values squared
1627  */
1628 template <typename Container>
1629 std::vector<double>
abssq(const Container & c,typename std::enable_if<is_iterable<Container>::value>::type * =nullptr)1630 abssq(const Container& c,
1631       typename std::enable_if<is_iterable<Container>::value>::type* = nullptr)
1632 // we need the std::enable_if to SFINAE out Eigen expressions
1633 // that will otherwise match, instead of matching
1634 // the overload below:
1635 
1636 // template<typename Derived>
1637 // abssq(const Eigen::MatrixBase<Derived>& A)
1638 {
1639     return abssq(std::begin(c), std::end(c));
1640 }
1641 
1642 /**
1643 * \brief Computes the absolute values squared of an Eigen expression
1644 
1645 * \param A Eigen expression
1646 * \return Real vector consisting of the absolute values squared
1647 */
1648 template <typename Derived>
abssq(const Eigen::MatrixBase<Derived> & A)1649 std::vector<double> abssq(const Eigen::MatrixBase<Derived>& A) {
1650     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
1651 
1652     // EXCEPTION CHECKS
1653 
1654     // check zero-size
1655     if (!internal::check_nonzero_size(rA))
1656         throw exception::ZeroSize("qpp::abssq()");
1657     // END EXCEPTION CHECKS
1658 
1659     return abssq(rA.data(), rA.data() + rA.size());
1660 }
1661 
1662 /**
1663  * \brief Element-wise sum of an STL-like range
1664  *
1665  * \param first Iterator to the first element of the range
1666  * \param last  Iterator to the last element of the range
1667  * \return Element-wise sum of the range, as a scalar over the same scalar
1668  * field as the range
1669  */
1670 template <typename InputIterator>
1671 typename std::iterator_traits<InputIterator>::value_type
sum(InputIterator first,InputIterator last)1672 sum(InputIterator first, InputIterator last) {
1673     using value_type = typename std::iterator_traits<InputIterator>::value_type;
1674 
1675     return std::accumulate(first, last, static_cast<value_type>(0));
1676 }
1677 
1678 /**
1679  * \brief Element-wise sum of the elements of an STL-like container
1680  *
1681  * \param c STL-like container
1682  * \return Element-wise sum of the elements of the container,
1683  * as a scalar over the same scalar field as the container
1684  */
1685 template <typename Container>
1686 typename Container::value_type
sum(const Container & c,typename std::enable_if<is_iterable<Container>::value>::type * =nullptr)1687 sum(const Container& c,
1688     typename std::enable_if<is_iterable<Container>::value>::type* = nullptr) {
1689     return sum(std::begin(c), std::end(c));
1690 }
1691 
1692 /**
1693  * \brief Element-wise product of an STL-like range
1694  *
1695  * \param first Iterator to the first element of the range
1696  * \param last  Iterator to the last element of the range
1697  * \return Element-wise product of the range, as a scalar over the same scalar
1698  * field as the range
1699  */
1700 template <typename InputIterator>
1701 typename std::iterator_traits<InputIterator>::value_type
prod(InputIterator first,InputIterator last)1702 prod(InputIterator first, InputIterator last) {
1703     using value_type = typename std::iterator_traits<InputIterator>::value_type;
1704 
1705     return std::accumulate(first, last, static_cast<value_type>(1),
1706                            std::multiplies<value_type>());
1707 }
1708 
1709 /**
1710  * \brief Element-wise product of the elements of an STL-like container
1711  *
1712  * \param c STL-like container
1713  * \return Element-wise product of the elements of the container, as a scalar
1714  * over the same scalar field as the container
1715  */
1716 template <typename Container>
1717 typename Container::value_type
prod(const Container & c,typename std::enable_if<is_iterable<Container>::value>::type * =nullptr)1718 prod(const Container& c,
1719      typename std::enable_if<is_iterable<Container>::value>::type* = nullptr) {
1720     return prod(std::begin(c), std::end(c));
1721 }
1722 
1723 /**
1724  * \brief Finds the pure state representation of a matrix proportional to a
1725  * projector onto a pure state
1726  *
1727  * \note No purity check is done, the input state \a A must have rank one,
1728  * otherwise the function returns the first non-zero eigenvector of \a A
1729  *
1730  * \param A Eigen expression, assumed to be proportional to a projector onto a
1731  * pure state, i.e., \a A is assumed to have rank one
1732  * \return The unique non-zero eigenvector of \a A (up to a phase), as a
1733  * dynamic column vector over the same scalar field as \a A
1734  */
1735 template <typename Derived>
1736 dyn_col_vect<typename Derived::Scalar>
rho2pure(const Eigen::MatrixBase<Derived> & A)1737 rho2pure(const Eigen::MatrixBase<Derived>& A) {
1738     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
1739 
1740     // EXCEPTION CHECKS
1741     // check zero-size
1742     if (!internal::check_nonzero_size(rA))
1743         throw exception::ZeroSize("qpp::rho2pure()");
1744     // check square matrix
1745     if (!internal::check_square_mat(rA))
1746         throw exception::MatrixNotSquare("qpp::rho2pure()");
1747     // END EXCEPTION CHECKS
1748 
1749     dyn_col_vect<double> tmp_evals = hevals(rA);
1750     cmat tmp_evects = hevects(rA);
1751     dyn_col_vect<typename Derived::Scalar> result =
1752         dyn_col_vect<typename Derived::Scalar>::Zero(rA.rows());
1753     // find the non-zero eigenvector
1754     // there is only one, assuming the state is pure
1755     for (idx k = 0; k < static_cast<idx>(rA.rows()); ++k) {
1756         if (std::abs(tmp_evals(k)) > 0) {
1757             result = tmp_evects.col(k);
1758             break;
1759         }
1760     }
1761 
1762     return result;
1763 }
1764 
1765 /**
1766  * \brief Constructs the complement of a subsystem vector
1767  *
1768  * \param subsys Subsystem vector
1769  * \param n Total number of systems
1770  * \return Complement of \a subsys with respect to the set
1771  * \f$\{0, 1, \ldots, n - 1\}\f$
1772  */
complement(std::vector<idx> subsys,idx n)1773 inline std::vector<idx> complement(std::vector<idx> subsys, idx n) {
1774     // EXCEPTION CHECKS
1775 
1776     if (n < subsys.size())
1777         throw exception::OutOfRange("qpp::complement()");
1778     for (idx s : subsys)
1779         if (s >= n)
1780             throw exception::SubsysMismatchDims("qpp::complement()");
1781     // END EXCEPTION CHECKS
1782 
1783     std::vector<idx> all(n);
1784     std::vector<idx> subsys_bar(n - subsys.size());
1785 
1786     std::iota(std::begin(all), std::end(all), 0);
1787     std::sort(std::begin(subsys), std::end(subsys));
1788     std::set_difference(std::begin(all), std::end(all), std::begin(subsys),
1789                         std::end(subsys), std::begin(subsys_bar));
1790 
1791     return subsys_bar;
1792 }
1793 
1794 /**
1795  * \brief Computes the 3-dimensional real Bloch vector corresponding to the
1796  * qubit density matrix \a A
1797  * \see qpp::bloch2rho()
1798  *
1799  * \note It is implicitly assumed that the density matrix is Hermitian
1800  *
1801  * \param A Eigen expression
1802  * \return 3-dimensional Bloch vector
1803  */
1804 template <typename Derived>
rho2bloch(const Eigen::MatrixBase<Derived> & A)1805 std::vector<double> rho2bloch(const Eigen::MatrixBase<Derived>& A) {
1806     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
1807 
1808     // EXCEPTION CHECKS
1809 
1810     // check qubit matrix
1811     if (!internal::check_qubit_matrix(rA))
1812         throw exception::NotQubitMatrix("qpp::rho2bloch()");
1813     // END EXCEPTION CHECKS
1814 
1815     std::vector<double> result(3);
1816     cmat X(2, 2), Y(2, 2), Z(2, 2);
1817     X << 0, 1, 1, 0;
1818     Y << 0, -1_i, 1_i, 0;
1819     Z << 1, 0, 0, -1;
1820     result[0] = std::real(trace(rA * X));
1821     result[1] = std::real(trace(rA * Y));
1822     result[2] = std::real(trace(rA * Z));
1823 
1824     return result;
1825 }
1826 
1827 /**
1828  * \brief Computes the density matrix corresponding to the 3-dimensional real
1829  * Bloch vector \a r
1830  * \see qpp::rho2bloch()
1831  *
1832  * \param r 3-dimensional real vector
1833  * \return Qubit density matrix
1834  */
bloch2rho(const std::vector<double> & r)1835 inline cmat bloch2rho(const std::vector<double>& r) {
1836     // EXCEPTION CHECKS
1837 
1838     // check 3-dimensional vector
1839     if (r.size() != 3)
1840         throw exception::CustomException("qpp::bloch2rho",
1841                                          "r is not a 3-dimensional vector!");
1842     // END EXCEPTION CHECKS
1843 
1844     cmat X(2, 2), Y(2, 2), Z(2, 2), Id2(2, 2);
1845     X << 0, 1, 1, 0;
1846     Y << 0, -1_i, 1_i, 0;
1847     Z << 1, 0, 0, -1;
1848     Id2 << 1, 0, 0, 1;
1849 
1850     return (Id2 + r[0] * X + r[1] * Y + r[2] * Z) / 2.;
1851 }
1852 
1853 /**
1854  * \brief Extracts the dits from a normalized multi-partite pure state in the
1855  * computational basis. Behaves like the "inverse" of qpp::mket().
1856  * \see qpp::mket()
1857  *
1858  * \note Assumes \a psi is a normalized state vector (ket) in the computational
1859  * basis, up to a phase; finds the first coefficient of \a psi close to 1 up to
1860  * \a precision, and returns the digit representation of the corresponding state
1861  * with a single 1 in that position. If there's no such coefficient (i.e., the
1862  * state is not a computational basis state up to a phase), returns the empty
1863  * vector.
1864  *
1865  * \param psi Column vector Eigen expression
1866  * \param dims Dimensions of the multi-partite system
1867  * \param precision Numerical precision (how close to 1 should the nonzero
1868  * coefficient be)
1869  * \return Vector containing the digit representation of \a psi, or the empty
1870  * vector if \a psi is not a  computational basis state up to a phase.
1871  */
1872 template <typename Derived>
zket2dits(const Eigen::MatrixBase<Derived> & psi,const std::vector<idx> & dims,double precision=1e-12)1873 std::vector<idx> zket2dits(const Eigen::MatrixBase<Derived>& psi,
1874                            const std::vector<idx>& dims,
1875                            double precision = 1e-12) {
1876     const dyn_col_vect<typename Derived::Scalar>& rpsi = psi.derived();
1877 
1878     // EXCEPTION CHECKS
1879 
1880     // check zero-size
1881     if (!internal::check_nonzero_size(rpsi))
1882         throw exception::ZeroSize("qpp::zket2dits()");
1883 
1884     // check column vector
1885     if (!internal::check_cvector(rpsi))
1886         throw exception::MatrixNotCvector("qpp::zket2dits()");
1887 
1888     // check that dims is a valid dimension vector
1889     if (!internal::check_dims(dims))
1890         throw exception::DimsInvalid("qpp::zket2dits()");
1891 
1892     // check that dims match psi column vector
1893     if (!internal::check_dims_match_cvect(dims, rpsi))
1894         throw exception::DimsMismatchCvector("qpp::zket2dits()");
1895     // END EXCEPTION CHECKS
1896 
1897     auto N = static_cast<idx>(rpsi.size());
1898     for (idx i = 0; i < N; ++i) {
1899         if (std::abs(std::abs(rpsi[i]) - 1.0) < precision) {
1900             return n2multiidx(i, dims);
1901         }
1902     }
1903 
1904     return {};
1905 }
1906 
1907 /**
1908  * \brief Extracts the dits from a normalized multi-partite pure state in the
1909  * computational basis, all subsystem having equal dimension \a d. Behaves like
1910  * the "inverse" of qpp::mket().
1911  * \see qpp::mket()
1912  *
1913  * \note Assumes \a psi is a normalized state vector (ket) in the computational
1914  * basis, up to a phase; finds the first coefficient of \a psi close to 1 up to
1915  * \a precision, and returns the digit representation of the corresponding state
1916  * with a single 1 in that position. If there's no such coefficient (i.e., the
1917  * state is not a computational basis state up to a phase), returns the empty
1918  * vector.
1919  *
1920  * \param psi Column vector Eigen expression
1921  * \param d Subsystem dimensions
1922  * \param precision Numerical precision (how close to 1 should the nonzero
1923  * coefficient be)
1924  * \return Vector containing the digit representation of \a psi, or the empty
1925  * vector if \a psi is not a  computational basis state up to a phase.
1926  */
1927 template <typename Derived>
zket2dits(const Eigen::MatrixBase<Derived> & psi,idx d=2,double precision=1e-12)1928 std::vector<idx> zket2dits(const Eigen::MatrixBase<Derived>& psi, idx d = 2,
1929                            double precision = 1e-12) {
1930     const dyn_col_vect<typename Derived::Scalar>& rpsi = psi.derived();
1931 
1932     // EXCEPTION CHECKS
1933     // check valid dims
1934     if (d < 2)
1935         throw exception::DimsInvalid("qpp::zket2dits()");
1936 
1937     // END EXCEPTION CHECKS
1938 
1939     idx n = internal::get_num_subsys(static_cast<idx>(rpsi.rows()), d);
1940     std::vector<idx> dims(n, d); // local dimensions vector
1941 
1942     return zket2dits(psi, dims, precision);
1943 }
1944 
1945 inline namespace literals {
1946 // Idea taken from http://techblog.altplus.co.jp/entry/2017/11/08/130921
1947 /**
1948  * \brief Multi-partite qubit ket user-defined literal
1949  * \see qpp::mket()
1950  *
1951  * Constructs the multi-partite qubit ket \f$|\mathrm{Bits}\rangle\f$
1952  *
1953  * \tparam Bits String of binary numbers representing the qubit ket
1954  * \return Multi-partite qubit ket, as a complex dynamic column vector
1955  */
1956 template <char... Bits>
operator ""_ket()1957 ket operator"" _ket() {
1958     constexpr idx n = sizeof...(Bits);
1959     constexpr char bits[n + 1] = {Bits..., '\0'};
1960     qpp::ket q = qpp::ket::Zero(static_cast<idx>(std::llround(std::pow(2, n))));
1961     idx pos = 0;
1962 
1963     // EXCEPTION CHECKS
1964 
1965     // check valid multi-partite qubit state
1966     for (idx i = 0; i < n; ++i) {
1967         if (bits[i] != '0' && bits[i] != '1')
1968             throw exception::OutOfRange(R"xxx(qpp::operator "" _ket())xxx");
1969     }
1970     // END EXCEPTION CHECKS
1971 
1972     pos = std::stoi(bits, nullptr, 2);
1973     q(pos) = 1;
1974 
1975     return q;
1976 }
1977 
1978 /**
1979  * \brief Multi-partite qubit bra user-defined literal
1980  * \see qpp::mket() and qpp::adjoint()
1981  *
1982  * Constructs the multi-partite qubit bra \f$\langle\mathrm{Bits}|\f$
1983  *
1984  * \tparam Bits String of binary numbers representing the qubit bra
1985  * \return Multi-partite qubit bra, as a complex dynamic row vector
1986  */
1987 template <char... Bits>
operator ""_bra()1988 bra operator"" _bra() {
1989     constexpr idx n = sizeof...(Bits);
1990     constexpr char bits[n + 1] = {Bits..., '\0'};
1991     qpp::bra q = qpp::ket::Zero(static_cast<idx>(std::llround(std::pow(2, n))));
1992     idx pos = 0;
1993 
1994     // EXCEPTION CHECKS
1995 
1996     // check valid multi-partite qubit state
1997     for (idx i = 0; i < n; ++i) {
1998         if (bits[i] != '0' && bits[i] != '1')
1999             throw exception::OutOfRange(R"xxx(qpp::operator "" _bra())xxx");
2000     }
2001     // END EXCEPTION CHECKS
2002 
2003     pos = std::stoi(bits, nullptr, 2);
2004     q(pos) = 1;
2005 
2006     return q;
2007 }
2008 
2009 /**
2010  * \brief Multi-partite qubit projector user-defined literal
2011  * \see qpp::mprj()
2012  *
2013  * Constructs the multi-partite qubit projector
2014  * \f$|\mathrm{Bits}\rangle\langle\mathrm{Bits}|\f$ (in the computational basis)
2015  *
2016  * \tparam Bits String of binary numbers representing the qubit state to project
2017  * on
2018  * \return Multi-partite qubit projector, as a complex dynamic matrix
2019  */
2020 template <char... Bits>
operator ""_prj()2021 cmat operator"" _prj() {
2022     constexpr idx n = sizeof...(Bits);
2023     constexpr char bits[n + 1] = {Bits..., '\0'};
2024 
2025     // EXCEPTION CHECKS
2026 
2027     // check valid multi-partite qubit state
2028     for (idx i = 0; i < n; ++i) {
2029         if (bits[i] != '0' && bits[i] != '1')
2030             throw exception::OutOfRange(R"xxx(qpp::operator "" _prj())xxx");
2031     }
2032     // END EXCEPTION CHECKS
2033 
2034     return kron(operator""_ket<Bits...>(), operator""_bra<Bits...>());
2035 }
2036 } /* namespace literals */
2037 
2038 namespace internal {
2039 // hash combine, code taken from boost::hash_combine(), see
2040 // https://www.boost.org/doc/libs/1_69_0/doc/html/hash/reference.html#boost.hash_combine
2041 /**
2042  * \brief Hash combine
2043  *
2044  * \tparam T Type
2045  * \param seed Initial seed, will be updated by the function
2046  * \param v Value with which the hash is combined
2047  */
2048 template <class T>
hash_combine(std::size_t & seed,const T & v)2049 void hash_combine(std::size_t& seed, const T& v) {
2050     std::hash<T> hasher;
2051     seed ^= hasher(v) + 0x9e3779b9 + (seed << static_cast<std::size_t>(6)) +
2052             (seed >> static_cast<std::size_t>(2));
2053 }
2054 } /* namespace internal */
2055 
2056 /**
2057  * \brief Computes the hash of en Eigen matrix/vector/expression
2058  * \note Code taken from boost::hash_combine(), see
2059  * https://www.boost.org/doc/libs/1_69_0/doc/html/hash/reference.html#boost.hash_combine
2060  *
2061  * \param A Eigen expression
2062  * \param seed Seed, 0 by default
2063  * \return Hash of its argument
2064  */
2065 template <typename Derived>
hash_eigen(const Eigen::MatrixBase<Derived> & A,std::size_t seed=0)2066 std::size_t hash_eigen(const Eigen::MatrixBase<Derived>& A,
2067                        std::size_t seed = 0) {
2068     const dyn_mat<typename Derived::Scalar>& rA = A.derived();
2069 
2070     // EXCEPTION CHECKS
2071 
2072     // check zero-size
2073     if (!internal::check_nonzero_size(rA))
2074         throw exception::ZeroSize("qpp::hash_eigen()");
2075     // END EXCEPTION CHECKS
2076 
2077     auto* p = rA.data();
2078     idx sizeA = static_cast<idx>(rA.size());
2079     for (idx i = 0; i < sizeA; ++i) {
2080         internal::hash_combine(seed, std::real(p[i]));
2081         internal::hash_combine(seed, std::imag(p[i]));
2082     }
2083 
2084     return seed;
2085 }
2086 
2087 namespace internal {
2088 /**
2089  * \class qpp::internal::HashEigen
2090  * \brief Functor for hashing Eigen expressions
2091  */
2092 struct HashEigen {
2093     template <typename Derived>
operator ()qpp::internal::HashEigen2094     std::size_t operator()(const Eigen::MatrixBase<Derived>& A) const {
2095         const dyn_mat<typename Derived::Scalar>& rA = A.derived();
2096         return hash_eigen(rA);
2097     }
2098 };
2099 
2100 /**
2101  * \class qpp::internal::EqualEigen
2102  * \brief Functor for comparing Eigen expressions for equality
2103  * \note Works without assertion fails even if the dimensions of the arguments
2104  * are different (in which case it simply returns false)
2105  */
2106 struct EqualEigen {
2107     template <typename Derived>
operator ()qpp::internal::EqualEigen2108     bool operator()(const Eigen::MatrixBase<Derived>& A,
2109                     const Eigen::MatrixBase<Derived>& B) const {
2110         const dyn_mat<typename Derived::Scalar>& rA = A.derived();
2111         const dyn_mat<typename Derived::Scalar>& rB = B.derived();
2112         if (rA.rows() == rB.rows() && rA.cols() == rB.cols())
2113             return rA == rB;
2114         else
2115             return false;
2116     }
2117 };
2118 
2119 /**
2120  * \class qpp::internal::EqualSameSizeStringDits
2121  * \brief Functor for comparing strings of numbers of equal sizes in
2122  * lexicographical order. Establishes a strict weak ordering relation.
2123  * \note Used as a hash table comparator in qpp::QEngine
2124  */
2125 struct EqualSameSizeStringDits {
operator ()qpp::internal::EqualSameSizeStringDits2126     bool operator()(const std::string& s1, const std::string& s2) const {
2127         std::vector<std::string> tk1, tk2;
2128         std::string w1, w2;
2129         std::stringstream ss1{s1}, ss2{s2};
2130 
2131         // tokenize the string into words (assumes words are separated by space)
2132         while (ss1 >> w1)
2133             tk1.emplace_back(w1);
2134         while (ss2 >> w2)
2135             tk2.emplace_back(w2);
2136 
2137         // compare lexicographically
2138         auto it1 = std::begin(tk1);
2139         auto it2 = std::begin(tk2);
2140         while (it1 != std::end(tk1) && it2 != std::end(tk2)) {
2141             auto n1 = std::stoll(*it1++);
2142             auto n2 = std::stoll(*it2++);
2143             if (n1 < n2)
2144                 return true;
2145             else if (n1 == n2)
2146                 continue;
2147             else if (n1 > n2)
2148                 return false;
2149         }
2150         return false;
2151     }
2152 };
2153 
2154 } /* namespace internal */
2155 } /* namespace qpp */
2156 
2157 #endif /* FUNCTIONS_HPP_ */
2158