LCOV - code coverage report
Current view: top level - pairinteraction - SystemBase.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 594 776 76.5 %
Date: 2024-04-29 00:41:50 Functions: 141 330 42.7 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2016 Sebastian Weber, Henri Menke. All rights reserved.
       3             :  *
       4             :  * This file is part of the pairinteraction library.
       5             :  *
       6             :  * The pairinteraction library is free software: you can redistribute it and/or modify
       7             :  * it under the terms of the GNU Lesser General Public License as published by
       8             :  * the Free Software Foundation, either version 3 of the License, or
       9             :  * (at your option) any later version.
      10             :  *
      11             :  * The pairinteraction library is distributed in the hope that it will be useful,
      12             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :  * GNU Lesser General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU Lesser General Public License
      17             :  * along with the pairinteraction library. If not, see <http://www.gnu.org/licenses/>.
      18             :  */
      19             : 
      20             : #include "SystemBase.hpp"
      21             : #include "Symmetry.hpp"
      22             : 
      23             : #include "EigenCompat.hpp"
      24             : #include <Eigen/Geometry>
      25             : #include <unsupported/Eigen/MatrixFunctions>
      26             : 
      27             : #include <numeric>
      28             : 
      29             : #ifdef WITH_INTEL_MKL
      30             : #define MKL_Complex8 std::complex<float>
      31             : #define MKL_Complex16 std::complex<double>
      32             : #include <mkl_lapacke.h>
      33             : #include <mkl_solvers_ee.h>
      34             : #elif defined EIGEN_USE_LAPACKE
      35             : #include <lapacke.h>
      36             : #endif // WITH_INTEL_MKL
      37             : 
      38             : #ifdef WITH_INTEL_MKL
      39           0 : static void feast_csrev(const char *uplo, const MKL_INT *n, const double *a, const MKL_INT *ia,
      40             :                         const MKL_INT *ja, MKL_INT *fpm, double *epsout, MKL_INT *loop,
      41             :                         const double *emin, const double *emax, MKL_INT *m0, double *e, double *x,
      42             :                         MKL_INT *m, double *res, MKL_INT *info) {
      43           0 :     dfeast_scsrev(uplo, n, a, ia, ja, fpm, epsout, loop, emin, emax, m0, e, x, m, res, info);
      44           0 : }
      45             : 
      46           2 : static void feast_csrev(const char *uplo, const MKL_INT *n, const MKL_Complex16 *a,
      47             :                         const MKL_INT *ia, const MKL_INT *ja, MKL_INT *fpm, double *epsout,
      48             :                         MKL_INT *loop, const double *emin, const double *emax, MKL_INT *m0,
      49             :                         double *e, MKL_Complex16 *x, MKL_INT *m, double *res, MKL_INT *info) {
      50           2 :     zfeast_hcsrev(uplo, n, a, ia, ja, fpm, epsout, loop, emin, emax, m0, e, x, m, res, info);
      51           2 : }
      52             : #endif // WITH_INTEL_MKL
      53             : 
      54             : #if defined EIGEN_USE_LAPACKE || WITH_INTEL_MKL
      55         259 : static int LAPACKE_evd(const int matrix_layout, const char jobz, const char uplo,
      56             :                        const lapack_int n, double *a, const lapack_int lda, double *w) {
      57         259 :     return LAPACKE_dsyevd(matrix_layout, jobz, uplo, n, a, lda, w);
      58             : }
      59             : 
      60          74 : static int LAPACKE_evd(const int matrix_layout, const char jobz, const char uplo,
      61             :                        const lapack_int n, lapack_complex_double *a, const lapack_int lda,
      62             :                        double *w) {
      63          74 :     return LAPACKE_zheevd(matrix_layout, jobz, uplo, n, a, lda, w);
      64             : }
      65             : #endif // EIGEN_USE_LAPACKE || WITH_INTEL_MKL
      66             : 
      67             : template <typename State>
      68             : State createStateFromLabel(const std::string &label);
      69             : template <>
      70           0 : StateOne createStateFromLabel<StateOne>(const std::string &label) {
      71           0 :     return StateOne(label);
      72             : }
      73             : template <>
      74          28 : StateTwo createStateFromLabel<StateTwo>(const std::string &label) {
      75          56 :     return StateTwo(std::array<std::string, 2>({{"0_" + label, "1_" + label}}));
      76             : }
      77             : 
      78             : // Implementation of SystemBase
      79             : 
      80             : template <class Scalar, class State>
      81           8 : void SystemBase<Scalar, State>::setMinimalNorm(const double &threshold) {
      82           8 :     threshold_for_sqnorm = threshold;
      83           8 : }
      84             : 
      85             : ////////////////////////////////////////////////////////////////////
      86             : /// Methods to restrict the number of states inside the basis //////
      87             : ////////////////////////////////////////////////////////////////////
      88             : 
      89             : template <class Scalar, class State>
      90          88 : void SystemBase<Scalar, State>::restrictEnergy(double e_min,
      91             :                                                double e_max) { // restricts diagonal entries
      92          88 :     energy_min = e_min;
      93          88 :     energy_max = e_max;
      94          88 : }
      95             : 
      96             : template <class Scalar, class State>
      97          43 : void SystemBase<Scalar, State>::restrictN(int n_min, int n_max) {
      98          43 :     this->range(range_n, n_min, n_max);
      99          43 : }
     100             : 
     101             : template <class Scalar, class State>
     102         150 : void SystemBase<Scalar, State>::restrictN(std::set<int> n) {
     103         150 :     range_n = n;
     104         150 : }
     105             : 
     106             : template <class Scalar, class State>
     107          42 : void SystemBase<Scalar, State>::restrictL(int l_min, int l_max) {
     108          42 :     this->range(range_l, l_min, l_max);
     109          42 : }
     110             : 
     111             : template <class Scalar, class State>
     112         150 : void SystemBase<Scalar, State>::restrictL(std::set<int> l) {
     113         150 :     range_l = l;
     114         150 : }
     115             : 
     116             : template <class Scalar, class State>
     117           5 : void SystemBase<Scalar, State>::restrictJ(float j_min, float j_max) {
     118           5 :     this->range(range_j, j_min, j_max);
     119           5 : }
     120             : 
     121             : template <class Scalar, class State>
     122         150 : void SystemBase<Scalar, State>::restrictJ(std::set<float> j) {
     123         150 :     range_j = j;
     124         150 : }
     125             : 
     126             : template <class Scalar, class State>
     127           2 : void SystemBase<Scalar, State>::restrictM(float m_min, float m_max) {
     128           2 :     this->range(range_m, m_min, m_max);
     129           2 : }
     130             : 
     131             : template <class Scalar, class State>
     132         150 : void SystemBase<Scalar, State>::restrictM(std::set<float> m) {
     133         150 :     range_m = m;
     134         150 : }
     135             : 
     136             : ////////////////////////////////////////////////////////////////////
     137             : /// Method for adding user-defined states //////////////////////////
     138             : ////////////////////////////////////////////////////////////////////
     139             : 
     140             : template <class Scalar, class State>
     141           0 : void SystemBase<Scalar, State>::addStates(const State &s) {
     142           0 :     states_to_add.insert(s);
     143           0 : }
     144             : 
     145             : template <class Scalar, class State>
     146           0 : void SystemBase<Scalar, State>::addStates(const std::set<State> &s) {
     147           0 :     states_to_add.insert(s.begin(), s.end());
     148           0 : }
     149             : 
     150             : // TODO make it possible to just use added states, i.e. use no restrictions on quantum numbers
     151             : // and energies
     152             : 
     153             : ////////////////////////////////////////////////////////////////////
     154             : /// Methods to get overlaps ////////////////////////////////////////
     155             : ////////////////////////////////////////////////////////////////////
     156             : 
     157             : template <class Scalar, class State>
     158         209 : Eigen::VectorX<double> SystemBase<Scalar, State>::getOverlap(const State &generalizedstate) {
     159         209 :     return this->getOverlap(generalizedstate, 0, 0, 0);
     160             : }
     161             : 
     162             : template <class Scalar, class State>
     163             : Eigen::VectorX<double>
     164           3 : SystemBase<Scalar, State>::getOverlap(const std::vector<State> &generalizedstates) {
     165           3 :     return this->getOverlap(generalizedstates, 0, 0, 0);
     166             : }
     167             : 
     168             : template <class Scalar, class State>
     169           0 : Eigen::VectorX<double> SystemBase<Scalar, State>::getOverlap(const size_t &state_index) {
     170           0 :     return this->getOverlap(state_index, 0, 0, 0);
     171             : }
     172             : 
     173             : template <class Scalar, class State>
     174             : Eigen::VectorX<double>
     175           0 : SystemBase<Scalar, State>::getOverlap(const std::vector<size_t> &states_indices) {
     176           0 :     return this->getOverlap(states_indices, 0, 0, 0);
     177             : }
     178             : 
     179             : template <class Scalar, class State>
     180           0 : Eigen::VectorX<double> SystemBase<Scalar, State>::getOverlap(const State &generalizedstate,
     181             :                                                              std::array<double, 3> to_z_axis,
     182             :                                                              std::array<double, 3> to_y_axis) {
     183           0 :     auto euler_zyz = this->getEulerAngles(to_z_axis, to_y_axis);
     184           0 :     return this->getOverlap(generalizedstate, euler_zyz[0], euler_zyz[1], euler_zyz[2]);
     185             : }
     186             : 
     187             : template <class Scalar, class State>
     188             : Eigen::VectorX<double>
     189           0 : SystemBase<Scalar, State>::getOverlap(const std::vector<State> &generalizedstates,
     190             :                                       std::array<double, 3> to_z_axis,
     191             :                                       std::array<double, 3> to_y_axis) {
     192           0 :     auto euler_zyz = this->getEulerAngles(to_z_axis, to_y_axis);
     193           0 :     return this->getOverlap(generalizedstates, euler_zyz[0], euler_zyz[1], euler_zyz[2]);
     194             : }
     195             : 
     196             : template <class Scalar, class State>
     197           0 : Eigen::VectorX<double> SystemBase<Scalar, State>::getOverlap(const size_t &state_index,
     198             :                                                              std::array<double, 3> to_z_axis,
     199             :                                                              std::array<double, 3> to_y_axis) {
     200           0 :     auto euler_zyz = this->getEulerAngles(to_z_axis, to_y_axis);
     201           0 :     return this->getOverlap(state_index, euler_zyz[0], euler_zyz[1], euler_zyz[2]);
     202             : }
     203             : 
     204             : template <class Scalar, class State>
     205             : Eigen::VectorX<double>
     206           0 : SystemBase<Scalar, State>::getOverlap(const std::vector<size_t> &states_indices,
     207             :                                       std::array<double, 3> to_z_axis,
     208             :                                       std::array<double, 3> to_y_axis) {
     209           0 :     auto euler_zyz = this->getEulerAngles(to_z_axis, to_y_axis);
     210           0 :     return this->getOverlap(states_indices, euler_zyz[0], euler_zyz[1], euler_zyz[2]);
     211             : }
     212             : 
     213             : template <class Scalar, class State>
     214         249 : Eigen::VectorX<double> SystemBase<Scalar, State>::getOverlap(const State &generalizedstate,
     215             :                                                              double alpha, double beta,
     216             :                                                              double gamma) {
     217         996 :     std::vector<State> generalizedstates({generalizedstate});
     218         498 :     return this->getOverlap(generalizedstates, alpha, beta, gamma);
     219             : }
     220             : 
     221             : template <class Scalar, class State>
     222           0 : Eigen::VectorX<double> SystemBase<Scalar, State>::getOverlap(const size_t &state_index,
     223             :                                                              double alpha, double beta,
     224             :                                                              double gamma) {
     225           0 :     std::vector<size_t> states_indices({state_index});
     226           0 :     return this->getOverlap(states_indices, alpha, beta, gamma);
     227             : }
     228             : 
     229             : template <class Scalar, class State>
     230             : Eigen::VectorX<double>
     231         254 : SystemBase<Scalar, State>::getOverlap(const std::vector<State> &generalizedstates, double alpha,
     232             :                                       double beta, double gamma) {
     233             :     // Build basis
     234         254 :     this->buildBasis();
     235             : 
     236             :     // Determine indices of the specified states
     237         508 :     std::vector<size_t> states_indices;
     238         254 :     states_indices.reserve(generalizedstates.size());
     239             : 
     240         513 :     for (const auto &searched_state : generalizedstates) {
     241         259 :         if (utils::is_true(searched_state.isGeneralized())) {
     242         305 :             for (const auto &state : states) {
     243         150 :                 if (state.state ^ searched_state) { // Check whether state.state is contained in
     244             :                                                     // searched_state
     245          10 :                     states_indices.push_back(state.idx);
     246             :                 }
     247             :             }
     248             :         } else {
     249         254 :             auto state_iter = states.template get<1>().find(searched_state);
     250         254 :             if (state_iter != states.template get<1>().end()) {
     251         249 :                 states_indices.push_back(state_iter->idx);
     252             :             }
     253             :         }
     254             :     }
     255             : 
     256             :     // Get the overlap
     257         508 :     return this->getOverlap(states_indices, alpha, beta, gamma);
     258             : }
     259             : 
     260             : template <class Scalar, class State>
     261             : Eigen::VectorX<double>
     262         254 : SystemBase<Scalar, State>::getOverlap(const std::vector<size_t> &states_indices, double alpha,
     263             :                                       double beta, double gamma) {
     264             :     // Build basis
     265         254 :     this->buildBasis();
     266             : 
     267             :     // Build state vectors
     268         508 :     Eigen::SparseMatrix<Scalar> overlap_states;
     269         254 :     if (alpha == 0 && beta == 0 && gamma == 0) {
     270         212 :         std::vector<Eigen::Triplet<Scalar>> overlap_states_triplets;
     271         212 :         overlap_states_triplets.reserve(states_indices.size());
     272             : 
     273         212 :         size_t current = 0;
     274         427 :         for (auto const &idx : states_indices) {
     275         215 :             overlap_states_triplets.emplace_back(idx, current++, 1);
     276             :         }
     277             : 
     278         212 :         overlap_states.resize(states.size(), states_indices.size());
     279         212 :         overlap_states.setFromTriplets(overlap_states_triplets.begin(),
     280         424 :                                        overlap_states_triplets.end());
     281             :     } else {
     282          42 :         overlap_states = this->rotateStates(states_indices, alpha, beta, gamma);
     283             :     }
     284             : 
     285             :     // Calculate overlap
     286         508 :     Eigen::SparseMatrix<Scalar> product = basisvectors.adjoint() * overlap_states;
     287         254 :     Eigen::VectorX<double> overlap = Eigen::VectorX<double>::Zero(product.rows());
     288         513 :     for (int k = 0; k < product.outerSize(); ++k) {
     289       11689 :         for (typename Eigen::SparseMatrix<Scalar>::InnerIterator triple(product, k); triple;
     290       11430 :              ++triple) {
     291       11430 :             overlap[triple.row()] += std::pow(std::abs(triple.value()), 2);
     292             :         }
     293             :     }
     294             : 
     295         508 :     return overlap;
     296             : }
     297             : 
     298             : ////////////////////////////////////////////////////////////////////
     299             : /// Methods to get properties of the system ////////////////////////
     300             : ////////////////////////////////////////////////////////////////////
     301             : 
     302             : template <class Scalar, class State>
     303          76 : std::vector<State> SystemBase<Scalar, State>::getStates() {
     304          76 :     this->buildBasis();
     305          76 :     std::vector<State> states_converted;
     306          76 :     states_converted.reserve(states.size());
     307       11284 :     for (const auto &s : states) {
     308        5604 :         states_converted.push_back(std::move(s.state));
     309             :     }
     310          76 :     return states_converted;
     311             : }
     312             : 
     313             : template <class Scalar, class State>
     314             : const typename states_set<State>::type &
     315     1307712 : SystemBase<Scalar, State>::getStatesMultiIndex() { // TODO @hmenke typemap for "const typename
     316             :                                                    // states_set<T>::type &"
     317     1307712 :     return states;
     318             : }
     319             : 
     320             : template <class Scalar, class State>
     321       74543 : Eigen::SparseMatrix<Scalar> &SystemBase<Scalar, State>::getBasisvectors() {
     322       74543 :     this->buildBasis();
     323       74543 :     return basisvectors;
     324             : }
     325             : 
     326             : template <class Scalar, class State>
     327      306548 : Eigen::SparseMatrix<Scalar> &SystemBase<Scalar, State>::getHamiltonian() {
     328      306548 :     this->buildHamiltonian();
     329      306548 :     return hamiltonian;
     330             : }
     331             : 
     332             : template <class Scalar, class State>
     333      295088 : size_t SystemBase<Scalar, State>::getNumBasisvectors() {
     334             :     // Build basis
     335      295088 :     this->buildBasis();
     336             : 
     337             :     // Check variables for consistency
     338      590176 :     if ((basisvectors.outerSize() != basisvectors.cols()) ||
     339      590176 :         (basisvectors.outerSize() != hamiltonian.rows()) ||
     340      295088 :         (basisvectors.outerSize() != hamiltonian.cols())) {
     341           0 :         throw std::runtime_error("Inconsistent variables at " + std::string(__FILE__) + ":" +
     342           0 :                                  std::to_string(__LINE__) + ".");
     343             :     }
     344             : 
     345      295088 :     return basisvectors.cols();
     346             : }
     347             : 
     348             : template <class Scalar, class State>
     349         325 : size_t SystemBase<Scalar, State>::getNumStates() {
     350             :     // Build basis
     351         325 :     this->buildBasis();
     352             : 
     353             :     // Check variables for consistency
     354         650 :     if ((basisvectors.innerSize() != basisvectors.rows()) ||
     355         325 :         (static_cast<size_t>(basisvectors.innerSize()) != states.size())) {
     356           0 :         throw std::runtime_error("Inconsistent variables at " + std::string(__FILE__) + ":" +
     357           0 :                                  std::to_string(__LINE__) + ".");
     358             :     }
     359             : 
     360         325 :     return basisvectors.rows();
     361             : }
     362             : 
     363             : template <class Scalar, class State>
     364          25 : std::vector<State> SystemBase<Scalar, State>::getMainStates() {
     365             :     // Build basis
     366          25 :     this->buildBasis();
     367             : 
     368             :     // Get states with the main contribution
     369          25 :     std::vector<State> states_with_maxval;
     370          25 :     states_with_maxval.reserve(basisvectors.cols());
     371             : 
     372        3383 :     for (int k = 0; k < basisvectors.outerSize(); ++k) { // col == idx_vector
     373        3358 :         double maxval = -1;
     374             :         size_t row_with_maxval;
     375             : 
     376       27612 :         for (typename Eigen::SparseMatrix<Scalar>::InnerIterator triple(basisvectors, k); triple;
     377       24254 :              ++triple) {
     378       24254 :             if (std::abs(triple.value()) > maxval) {
     379        4791 :                 row_with_maxval = triple.row();
     380        4791 :                 maxval = std::abs(triple.value());
     381             :             }
     382             :         }
     383             : 
     384        3358 :         states_with_maxval.push_back(states[row_with_maxval].state);
     385             :     }
     386             : 
     387          25 :     return states_with_maxval;
     388             : }
     389             : 
     390             : template <class Scalar, class State>
     391             : std::array<std::vector<size_t>, 2>
     392           8 : SystemBase<Scalar, State>::getConnections(SystemBase<Scalar, State> &system_to, double threshold) {
     393             :     // Build basis
     394           8 :     this->buildBasis();
     395           8 :     system_to.buildBasis();
     396             : 
     397           8 :     double threshold_sqrt = std::sqrt(threshold);
     398             : 
     399             :     // Calculate transformator between the set of states
     400          16 :     std::vector<Eigen::Triplet<Scalar>> triplets_transformator;
     401           8 :     triplets_transformator.reserve(std::min(states.size(), system_to.states.size()));
     402             : 
     403         768 :     for (const auto &s : system_to.states) {
     404         380 :         auto state_iter = states.template get<1>().find(s.state);
     405         380 :         if (state_iter != states.template get<1>().end()) {
     406         346 :             size_t idx_from = state_iter->idx;
     407         346 :             triplets_transformator.emplace_back(idx_from, s.idx, 1);
     408             :         }
     409             :     }
     410             : 
     411          16 :     Eigen::SparseMatrix<Scalar> transformator(states.size(), system_to.states.size());
     412           8 :     transformator.setFromTriplets(triplets_transformator.begin(), triplets_transformator.end());
     413             : 
     414             :     // Calculate overlap
     415          16 :     Eigen::SparseMatrix<double> overlap_sqrt =
     416           8 :         (basisvectors.adjoint() * transformator * system_to.basisvectors).cwiseAbs();
     417             : 
     418             :     // Determine the indices of the maximal values within the rows
     419          16 :     std::vector<size_t> rows_with_maxval;
     420           8 :     rows_with_maxval.reserve(overlap_sqrt.cols());
     421             : 
     422         168 :     for (int k = 0; k < overlap_sqrt.outerSize(); ++k) {
     423         160 :         double maxval = -1;
     424             :         size_t row_with_maxval;
     425             : 
     426        1311 :         for (typename Eigen::SparseMatrix<double>::InnerIterator triple(overlap_sqrt, k); triple;
     427        1151 :              ++triple) {
     428        1151 :             if (triple.value() > maxval) {
     429         445 :                 row_with_maxval = triple.row();
     430         445 :                 maxval = triple.value();
     431             :             }
     432             :         }
     433             : 
     434         160 :         rows_with_maxval.push_back(row_with_maxval);
     435             :     }
     436             : 
     437             :     // Determine the maximal values within the columns and construct connections
     438          16 :     Eigen::SparseMatrix<double> overlap_sqrt_transposed = overlap_sqrt.transpose();
     439           8 :     std::array<std::vector<size_t>, 2> connections;
     440           8 :     connections[0].reserve(std::max(overlap_sqrt.rows(), overlap_sqrt.cols()));
     441           8 :     connections[1].reserve(std::max(overlap_sqrt.rows(), overlap_sqrt.cols()));
     442             : 
     443         168 :     for (int k = 0; k < overlap_sqrt_transposed.outerSize(); ++k) { // cols
     444         160 :         double maxval = threshold_sqrt;
     445             :         size_t idx_from;
     446             :         size_t idx_to;
     447             : 
     448        1311 :         for (typename Eigen::SparseMatrix<double>::InnerIterator triple(overlap_sqrt_transposed, k);
     449        1151 :              triple; ++triple) {
     450        1151 :             if (triple.value() > maxval) {
     451         335 :                 idx_from = triple.col();
     452         335 :                 idx_to = triple.row();
     453         335 :                 maxval = triple.value();
     454             :             }
     455             :         }
     456             : 
     457         160 :         if (maxval > threshold_sqrt && rows_with_maxval[idx_to] == idx_from) {
     458         158 :             connections[0].push_back(idx_from);
     459         158 :             connections[1].push_back(idx_to);
     460             :         }
     461             :     }
     462             : 
     463           8 :     connections[0].shrink_to_fit();
     464           8 :     connections[1].shrink_to_fit();
     465             : 
     466          16 :     return connections;
     467             : }
     468             : 
     469             : ////////////////////////////////////////////////////////////////////
     470             : /// Methods to build, transform, and destroy the system ////////////
     471             : ////////////////////////////////////////////////////////////////////
     472             : 
     473             : template <class Scalar, class State>
     474      307047 : void SystemBase<Scalar, State>::buildHamiltonian() {
     475             :     // Build basis, also constructs the Hamiltonian matrix without interaction
     476      307047 :     this->buildBasis();
     477             : 
     478             :     // Initialize Hamiltonian matrix with interaction if required
     479      307047 :     if (is_new_hamiltonian_required) {
     480         346 :         if (is_interaction_already_contained) {
     481             : 
     482             :             // Check variables for consistency
     483         474 :             if (memory_saving || basisvectors_unperturbed_cache.size() == 0 ||
     484         237 :                 hamiltonian_unperturbed_cache.size() == 0) {
     485           0 :                 throw std::runtime_error("Inconsistent variables at " + std::string(__FILE__) +
     486           0 :                                          ":" + std::to_string(__LINE__) + ".");
     487             :             }
     488             : 
     489             :             // Reset the Hamiltonian if it already contains the interaction
     490         237 :             basisvectors = basisvectors_unperturbed_cache;
     491         237 :             hamiltonian = hamiltonian_unperturbed_cache;
     492         109 :         } else if (!memory_saving) {
     493             : 
     494             :             // Store the Hamiltonian without interaction
     495         109 :             basisvectors_unperturbed_cache = basisvectors;
     496         109 :             hamiltonian_unperturbed_cache = hamiltonian;
     497             :         }
     498             : 
     499             :         // Build interaction
     500         346 :         this->initializeInteraction();
     501             : 
     502             :         // Add interaction to the Hamiltonian
     503         346 :         this->addInteraction();
     504             : 
     505         346 :         if (memory_saving) {
     506             :             // Delete the variables used to add the interaction to the Hamiltonian
     507           0 :             this->deleteInteraction();
     508             :         }
     509             : 
     510         346 :         is_interaction_already_contained = true;
     511         346 :         is_new_hamiltonian_required = false;
     512             :     }
     513      307047 : }
     514             : 
     515             : template <class Scalar, class State>
     516           9 : void SystemBase<Scalar, State>::buildInteraction() {
     517             :     // Build basis
     518           9 :     this->buildBasis();
     519             : 
     520             :     // Initialize interaction
     521           9 :     this->initializeInteraction(); // this method checks by itself whether a new initialization
     522             :                                    // is required
     523           9 : }
     524             : 
     525             : template <class Scalar, class State>
     526      677929 : void SystemBase<Scalar, State>::buildBasis() {
     527             :     // Check variables for consistency
     528     1355858 :     if (((hamiltonian.size() == 0) != states.empty()) ||
     529      677929 :         ((hamiltonian.size() == 0) != (basisvectors.size() == 0))) {
     530           0 :         throw std::runtime_error("Inconsistent variables at " + std::string(__FILE__) + ":" +
     531           0 :                                  std::to_string(__LINE__) + ".");
     532             :     }
     533             : 
     534             :     // In case of no new basis restrictions and already initialized basis, there is nothing to
     535             :     // do
     536     2033459 :     if (!states.empty() && states_to_add.empty() && range_n.empty() && range_l.empty() &&
     537     2711224 :         range_j.empty() && range_m.empty() && energy_min == std::numeric_limits<double>::lowest() &&
     538      677765 :         energy_max == std::numeric_limits<double>::max()) { // TODO check for new threshold, too
     539      677765 :         return;
     540             :     }
     541             : 
     542             :     // Check whether the basis does not exist
     543         164 :     if (states.empty()) {
     544             : 
     545             :         // Initialize the basis
     546         164 :         this->initializeBasis();
     547             : 
     548             :         // Forget basis restrictions as they were applied now
     549         164 :         this->forgetRestrictions();
     550             : 
     551             :     } else {
     552           0 :         this->updateEverything();
     553             :     }
     554             : 
     555             :     // Check whether the basis is empty
     556         164 :     if (basisvectors.rows() == 0) {
     557           0 :         throw std::runtime_error("The basis contains no states.");
     558             :     }
     559         164 :     if (basisvectors.cols() == 0) {
     560           0 :         throw std::runtime_error("The basis contains no vectors.");
     561             :     }
     562             : }
     563             : 
     564             : template <class Scalar, class State>
     565           1 : void SystemBase<Scalar, State>::diagonalize(double energy_lower_bound, double energy_upper_bound) {
     566           1 :     this->diagonalize(energy_lower_bound, energy_upper_bound, 0);
     567           1 : }
     568             : 
     569             : template <class Scalar, class State>
     570           2 : void SystemBase<Scalar, State>::diagonalize(double energy_lower_bound, double energy_upper_bound,
     571             :                                             double threshold) {
     572             : #ifdef WITH_INTEL_MKL
     573           2 :     this->buildHamiltonian();
     574             : 
     575             :     // Check if already diagonal
     576           2 :     if (checkIsDiagonal(hamiltonian)) {
     577           0 :         return;
     578             :     }
     579             : 
     580             :     // Estimate number of found eigenvalues
     581           4 :     std::vector<double> diagonal_max(hamiltonian.outerSize(), 0);
     582           4 :     std::vector<double> diagonal_val;
     583           4 :     std::vector<int> diagonal_idx;
     584           2 :     diagonal_val.reserve(hamiltonian.outerSize());
     585           2 :     diagonal_idx.reserve(hamiltonian.outerSize());
     586          25 :     for (int k = 0; k < hamiltonian.outerSize(); ++k) {
     587          23 :         double val = 0;
     588         268 :         for (typename Eigen::SparseMatrix<Scalar>::InnerIterator triple(hamiltonian, k); triple;
     589         245 :              ++triple) {
     590         245 :             if (triple.row() == triple.col()) {
     591          23 :                 val = std::real(triple.value());
     592         222 :             } else if (triple.row() != triple.col()) {
     593         222 :                 diagonal_max[k] = std::max(diagonal_max[k], std::abs(triple.value()));
     594             :             }
     595             :         }
     596          23 :         diagonal_idx.push_back(k);
     597          23 :         diagonal_val.push_back(val);
     598             :     }
     599             : 
     600           2 :     MKL_INT m0 = std::count_if(diagonal_val.begin(), diagonal_val.end(),
     601          43 :                                [&energy_lower_bound, &energy_upper_bound](const double &val) {
     602          23 :                                    return (val < energy_upper_bound) && (val > energy_lower_bound);
     603           4 :                                }) +
     604           2 :         std::count_if(diagonal_idx.begin(), diagonal_idx.end(),
     605         100 :                       [&energy_lower_bound, &energy_upper_bound, &diagonal_val,
     606             :                        &diagonal_max](const int &i) {
     607          23 :                           return ((diagonal_val[i] >= energy_upper_bound) ||
     608          20 :                                   (diagonal_val[i] <= energy_lower_bound)) &&
     609          45 :                               (diagonal_val[i] < energy_upper_bound + diagonal_max[i]) &&
     610          25 :                               (diagonal_val[i] > energy_lower_bound - diagonal_max[i]);
     611             :                       });
     612             : 
     613             :     // Inplace conversion of the Hamiltonian to CSR with one-based indexing
     614           2 :     hamiltonian = hamiltonian.transpose();
     615           2 :     hamiltonian.makeCompressed();
     616          27 :     for (int i = 0; i < hamiltonian.rows() + 1; ++i) {
     617          25 :         hamiltonian.outerIndexPtr()[i] += 1;
     618             :     }
     619         247 :     for (int i = 0; i < hamiltonian.nonZeros(); ++i) {
     620         245 :         hamiltonian.innerIndexPtr()[i] += 1;
     621             :     }
     622             : 
     623             :     // Set default parameters for the diagonalization as described at
     624             :     // https://software.intel.com/en-us/mkl-developer-reference-c-extended-eigensolver-input-parameters.
     625             : 
     626           4 :     std::vector<MKL_INT> fpm(128);
     627           2 :     feastinit(&fpm[0]);
     628           2 :     fpm[0] = 1;  // enables terminal output
     629           2 :     fpm[1] = 6;  // number of contour points
     630           2 :     fpm[26] = 0; // disables matrix checker
     631           2 :     fpm[3] = 5;  // maximum number of refinement loops allowed
     632           2 :     if (threshold != 0) {
     633             :         // Adapt the error trace stopping criteria (10-fpm[2])
     634           1 :         fpm[2] = std::min(std::round(-std::log10(threshold)), 12.);
     635             :     }
     636             : 
     637             :     // Do the diagonalization
     638             :     {
     639           2 :         MKL_INT n = hamiltonian.rows(); // size of the matrix
     640             :         MKL_INT m;                      // will contain the number of eigenvalues
     641           4 :         std::vector<Scalar> x(m0 * n);  // the first m columns will contain the eigenvectors
     642             :         {
     643           4 :             std::vector<double> e(m0); // will contain the first m eigenvalues
     644             :             {
     645           2 :                 char uplo = 'F';             // full matrix is stored
     646             :                 MKL_INT info;                // will contain return codes
     647             :                 double epsout;               // will contain relative error
     648             :                 MKL_INT loop;                // will contain number of used refinement
     649           4 :                 std::vector<double> res(m0); // will contain the residual errors
     650             : 
     651           2 :                 feast_csrev(&uplo, &n, hamiltonian.valuePtr(), hamiltonian.outerIndexPtr(),
     652           2 :                             hamiltonian.innerIndexPtr(), &fpm[0], &epsout, &loop,
     653           2 :                             &energy_lower_bound, &energy_upper_bound, &m0, &e[0], &x[0], &m,
     654           2 :                             &res[0], &info);
     655           2 :                 if (info != 0) {
     656           0 :                     throw std::runtime_error("Diagonalization with FEAST failed.");
     657             :                 }
     658             :             }
     659             : 
     660             :             // Build the new hamiltonian
     661           2 :             hamiltonian.resize(m, m);
     662           2 :             hamiltonian.setZero();
     663           2 :             hamiltonian.reserve(m);
     664          20 :             for (int idx = 0; idx < m; ++idx) {
     665          18 :                 hamiltonian.insert(idx, idx) = e[idx];
     666             :             }
     667           2 :             hamiltonian.makeCompressed();
     668             :         }
     669             : 
     670             :         // Transform the basis vectors
     671           4 :         Eigen::SparseMatrix<Scalar> evecs =
     672           4 :             Eigen::Map<Eigen::MatrixX<Scalar>>(&x[0], n, m).sparseView();
     673           2 :         if (threshold == 0) {
     674           1 :             basisvectors = basisvectors * evecs;
     675             :         } else {
     676           1 :             basisvectors = (basisvectors * evecs).pruned(threshold, 1);
     677             :         }
     678             :     }
     679             : #else  // WITH_INTEL_MKL
     680             :     (void)energy_lower_bound;
     681             :     (void)energy_upper_bound;
     682             :     (void)threshold;
     683             :     throw std::runtime_error(
     684             :         "The method does not work because the program was compiled without MKL support.");
     685             : #endif // WITH_INTEL_MKL
     686             : }
     687             : 
     688             : template <class Scalar, class State>
     689         216 : void SystemBase<Scalar, State>::diagonalize() {
     690         216 :     this->diagonalize(0);
     691         216 : }
     692             : 
     693             : template <class Scalar, class State>
     694         477 : void SystemBase<Scalar, State>::diagonalize(double threshold) {
     695         477 :     this->buildHamiltonian();
     696             : 
     697             :     // Check if already diagonal
     698         477 :     if (checkIsDiagonal(hamiltonian)) {
     699         144 :         return;
     700             :     }
     701             : 
     702             : #if defined EIGEN_USE_LAPACKE || WITH_INTEL_MKL
     703             : 
     704             :     // Diagonalize hamiltonian
     705         333 :     char jobz = 'V';                          // eigenvalues and eigenvectors are computed
     706         333 :     char uplo = 'U';                          // full matrix is stored, upper is used
     707         333 :     int n = hamiltonian.cols();               // size of the matrix
     708         666 :     Eigen::MatrixX<Scalar> mat = hamiltonian; // matrix
     709         333 :     int lda = mat.outerStride();              // leading dimension
     710         666 :     Eigen::VectorX<double> evals(n);          // eigenvalues
     711         333 :     int info = LAPACKE_evd(LAPACK_COL_MAJOR, jobz, uplo, n, mat.data(), lda, evals.data());
     712         333 :     if (info != 0) {
     713           0 :         throw std::runtime_error("Diagonalization with LAPACKE failed.");
     714             :     }
     715             : 
     716             :     // Get eigenvectors
     717         666 :     Eigen::SparseMatrix<Scalar> evecs = mat.sparseView();
     718             : 
     719             : #else // EIGEN_USE_LAPACKE || WITH_INTEL_MKL
     720             : 
     721             :     // Diagonalize hamiltonian
     722             :     Eigen::SelfAdjointEigenSolver<Eigen::MatrixX<Scalar>> eigensolver(hamiltonian);
     723             : 
     724             :     // Get eigenvalues and eigenvectors
     725             :     Eigen::VectorX<double> evals = eigensolver.eigenvalues();
     726             :     Eigen::SparseMatrix<Scalar> evecs = eigensolver.eigenvectors().sparseView();
     727             : 
     728             : #endif // EIGEN_USE_LAPACKE || WITH_INTEL_MKL
     729             : 
     730             :     // Build the new hamiltonian
     731         333 :     hamiltonian.setZero();
     732         333 :     hamiltonian.reserve(evals.size());
     733       17284 :     for (int idx = 0; idx < evals.size(); ++idx) {
     734       16951 :         hamiltonian.insert(idx, idx) = evals.coeffRef(idx);
     735             :     }
     736         333 :     hamiltonian.makeCompressed();
     737             : 
     738             :     // Transform the basis vectors
     739         333 :     if (threshold == 0) {
     740          79 :         basisvectors = basisvectors * evecs;
     741             :     } else {
     742         254 :         basisvectors = (basisvectors * evecs).pruned(threshold, 1);
     743             :     }
     744             : 
     745             :     // TODO call transformInteraction (see applyRightsideTransformator), perhaps not?
     746             : }
     747             : 
     748             : template <class Scalar, class State>
     749           0 : void SystemBase<Scalar, State>::canonicalize() {
     750           0 :     this->buildHamiltonian();
     751             : 
     752             :     // Transform the hamiltonian
     753           0 :     hamiltonian = basisvectors * hamiltonian * basisvectors.adjoint();
     754             : 
     755             :     // Transform the basis vectors
     756           0 :     basisvectors = basisvectors * basisvectors.adjoint();
     757             : 
     758             :     // TODO call transformInteraction (see applyRightsideTransformator), perhaps yes?
     759           0 : }
     760             : 
     761             : template <class Scalar, class State>
     762           2 : void SystemBase<Scalar, State>::unitarize() {
     763           2 :     this->buildHamiltonian();
     764             : 
     765             :     // Determine hash of the list of states
     766             :     size_t hashvalue_states =
     767           2 :         utils::hash_range(states.template get<0>().begin(), states.template get<0>().end());
     768             : 
     769             :     // Loop over basisvectors
     770           2 :     size_t num_basisvectors = basisvectors.outerSize();
     771             : 
     772           2 :     states.clear();
     773           2 :     size_t idx_new = 0;
     774          30 :     for (int k = 0; k < basisvectors.outerSize(); ++k) { // col == idx_vector
     775          28 :         size_t hashvalue = hashvalue_states;
     776         160 :         for (typename Eigen::SparseMatrix<Scalar>::InnerIterator triple(basisvectors, k); triple;
     777         132 :              ++triple) {
     778         132 :             utils::hash_combine(hashvalue, triple.row());
     779         132 :             utils::hash_combine(hashvalue, triple.value());
     780             :         }
     781             : 
     782             :         // Rewrite basisvectors as states
     783          28 :         std::stringstream ss;
     784          28 :         ss << std::hex << hashvalue;
     785          28 :         states.push_back(enumerated_state<State>(idx_new++, createStateFromLabel<State>(ss.str())));
     786             :     }
     787           2 :     states.shrink_to_fit();
     788             : 
     789           2 :     if (num_basisvectors != states.size()) {
     790           0 :         throw std::runtime_error("A hash collision occurred.");
     791             :     }
     792             : 
     793             :     // Adapt basisvectors
     794           2 :     basisvectors.resize(states.size(), states.size());
     795           2 :     basisvectors.setZero();
     796           2 :     basisvectors.reserve(states.size());
     797          30 :     for (size_t idx = 0; idx < states.size(); ++idx) {
     798          28 :         basisvectors.insert(idx, idx) = 1;
     799             :     }
     800           2 :     basisvectors.makeCompressed();
     801             : 
     802             :     // Delete caches as they are no longer meaningful
     803           2 :     basisvectors_unperturbed_cache.resize(0, 0);
     804           2 :     hamiltonian_unperturbed_cache.resize(0, 0);
     805           2 : }
     806             : 
     807             : template <class Scalar, class State>
     808           1 : void SystemBase<Scalar, State>::rotate(std::array<double, 3> to_z_axis,
     809             :                                        std::array<double, 3> to_y_axis) {
     810           1 :     auto euler_zyz = this->getEulerAngles(to_z_axis, to_y_axis);
     811           1 :     this->rotate(euler_zyz[0], euler_zyz[1], euler_zyz[2]);
     812           1 : }
     813             : 
     814             : template <class Scalar, class State>
     815          10 : void SystemBase<Scalar, State>::rotate(double alpha, double beta,
     816             :                                        double gamma) { // TODO applyRightsideTransformator ?
     817             :     // Build Hamiltonian and basis
     818          10 :     this->buildHamiltonian();
     819             : 
     820             :     // Get the rotator for the basis states
     821          20 :     Eigen::SparseMatrix<Scalar> transformator = this->buildStaterotator(alpha, beta, gamma);
     822             : 
     823             :     // Rotate basis
     824          10 :     this->transformInteraction(basisvectors.adjoint());
     825             : 
     826          10 :     basisvectors = transformator * basisvectors;
     827          10 :     if (basisvectors_unperturbed_cache.size() != 0) {
     828           7 :         basisvectors_unperturbed_cache = transformator * basisvectors_unperturbed_cache;
     829             :     }
     830             : 
     831          10 :     this->transformInteraction(basisvectors);
     832          10 : }
     833             : 
     834             : template <class Scalar, class State>
     835          38 : void SystemBase<Scalar, State>::add(SystemBase<Scalar, State> &system) {
     836             :     // --- Build bases ---
     837          38 :     this->buildBasis();
     838          38 :     system.buildBasis();
     839             : 
     840          38 :     size_t size_this = basisvectors.cols();
     841          38 :     size_t size_other = system.basisvectors.cols();
     842             : 
     843             :     // --- Combine system specific variables ---
     844          38 :     this->incorporate(system);
     845             : 
     846             :     // --- Combine universal variables ---
     847          38 :     if (memory_saving != system.memory_saving) {
     848           0 :         throw std::runtime_error(
     849             :             "The value of the variable 'memory_saving' must be the same for both systems.");
     850             :     }
     851          38 :     if (is_interaction_already_contained != system.is_interaction_already_contained) {
     852           0 :         throw std::runtime_error("The value of the variable 'is_interaction_already_contained' "
     853             :                                  "must be the same for both systems.");
     854             :     }
     855          38 :     if (is_new_hamiltonian_required != system.is_new_hamiltonian_required) {
     856           0 :         throw std::runtime_error(
     857             :             "The value of the variable 'is_new_hamiltonian_required' must be the same "
     858             :             "for both systems.");
     859             :     }
     860             : 
     861             :     // --- Combine states and build transformators ---
     862          76 :     Eigen::SparseMatrix<Scalar> transformator;
     863             :     {
     864          38 :         std::vector<Eigen::Triplet<Scalar>> transformator_triplets;
     865          38 :         transformator_triplets.reserve(system.states.size());
     866             : 
     867        2115 :         for (const auto &entry : system.states) {
     868        2077 :             auto state_iter = states.template get<1>().find(entry.state);
     869             : 
     870        2077 :             size_t newidx = states.size();
     871        2077 :             if (state_iter == states.template get<1>().end()) {
     872         495 :                 states.push_back(enumerated_state<State>(newidx, entry.state));
     873             :             } else {
     874        1582 :                 newidx = state_iter->idx;
     875             :             }
     876             : 
     877        2077 :             transformator_triplets.emplace_back(newidx, entry.idx, 1);
     878             :         }
     879             : 
     880          38 :         transformator.resize(states.size(), system.basisvectors.rows());
     881          38 :         transformator.setFromTriplets(transformator_triplets.begin(), transformator_triplets.end());
     882             :     }
     883             : 
     884             :     // --- Combine basisvectors and basisvectors_unperturbed_cache ---
     885          38 :     basisvectors.conservativeResize(states.size(), size_this + size_other);
     886          38 :     basisvectors.rightCols(size_other) = transformator * system.basisvectors;
     887             : 
     888          38 :     if ((basisvectors_unperturbed_cache.size() != 0) !=
     889          38 :         (system.basisvectors_unperturbed_cache.size() != 0)) {
     890           0 :         throw std::runtime_error("Inconsistent variables at " + std::string(__FILE__) + ":" +
     891           0 :                                  std::to_string(__LINE__) + ".");
     892             :     }
     893             : 
     894          38 :     if (basisvectors_unperturbed_cache.size() != 0) {
     895          27 :         basisvectors_unperturbed_cache.conservativeResize(
     896          27 :             states.size(),
     897          27 :             basisvectors_unperturbed_cache.cols() + system.basisvectors_unperturbed_cache.cols());
     898          27 :         basisvectors_unperturbed_cache.rightCols(system.basisvectors_unperturbed_cache.cols()) =
     899          54 :             transformator * system.basisvectors_unperturbed_cache;
     900             :     }
     901             : 
     902             :     // --- Check if basis vectors are orthogonal ---
     903          76 :     Eigen::SparseMatrix<Scalar> tmp =
     904          38 :         (basisvectors.leftCols(size_this).adjoint() * basisvectors.rightCols(size_other))
     905          38 :             .pruned(1e-12, 1);
     906          38 :     if (tmp.nonZeros() != 0) {
     907           0 :         throw std::runtime_error(
     908             :             "Two systems cannot be combined if their basis vectors are not orthogonal.");
     909             :     }
     910             : 
     911             :     // --- Combine hamiltonian and hamiltonian_unperturbed_cache ---
     912          76 :     Eigen::SparseMatrix<Scalar> shifter(hamiltonian.rows() + system.hamiltonian.rows(),
     913             :                                         system.hamiltonian.rows());
     914             :     {
     915          38 :         std::vector<Eigen::Triplet<Scalar>> shifter_triplets;
     916          38 :         shifter_triplets.reserve(system.hamiltonian.rows());
     917             : 
     918         960 :         for (Eigen::Index idx = 0; idx < system.hamiltonian.rows(); ++idx) {
     919         922 :             shifter_triplets.emplace_back(hamiltonian.rows() + idx, idx, 1);
     920             :         }
     921             : 
     922          38 :         shifter.setFromTriplets(shifter_triplets.begin(), shifter_triplets.end());
     923             :     }
     924             : 
     925          38 :     hamiltonian.conservativeResize(hamiltonian.rows() + system.hamiltonian.rows(),
     926          38 :                                    hamiltonian.cols() + system.hamiltonian.cols());
     927          38 :     hamiltonian.rightCols(system.hamiltonian.cols()) = shifter * system.hamiltonian;
     928             : 
     929          38 :     if ((hamiltonian_unperturbed_cache.size() != 0) !=
     930          38 :         (system.hamiltonian_unperturbed_cache.size() != 0)) {
     931           0 :         throw std::runtime_error("Inconsistent variables at " + std::string(__FILE__) + ":" +
     932           0 :                                  std::to_string(__LINE__) + ".");
     933             :     }
     934             : 
     935          38 :     if (hamiltonian_unperturbed_cache.size() != 0) {
     936          27 :         hamiltonian_unperturbed_cache.conservativeResize(
     937          27 :             hamiltonian_unperturbed_cache.rows() + system.hamiltonian_unperturbed_cache.rows(),
     938          27 :             hamiltonian_unperturbed_cache.cols() + system.hamiltonian_unperturbed_cache.cols());
     939          27 :         hamiltonian_unperturbed_cache.rightCols(system.hamiltonian_unperturbed_cache.cols()) =
     940          54 :             shifter * system.hamiltonian_unperturbed_cache;
     941             :     }
     942          38 : }
     943             : 
     944             : template <class Scalar, class State>
     945           4 : void SystemBase<Scalar, State>::constrainBasisvectors(
     946             :     std::vector<size_t> indices_of_wanted_basisvectors) {
     947           4 :     this->buildHamiltonian();
     948             : 
     949             :     // Check if indices are unique
     950             :     {
     951           8 :         std::set<size_t> tmp(indices_of_wanted_basisvectors.begin(),
     952             :                              indices_of_wanted_basisvectors.end());
     953           4 :         if (tmp.size() < indices_of_wanted_basisvectors.size()) {
     954           0 :             throw std::runtime_error("Indices are occuring multiple times.");
     955             :         }
     956             :     }
     957             : 
     958             :     // Build transformator and remove vectors
     959           8 :     std::vector<Eigen::Triplet<Scalar>> triplets_transformator;
     960           4 :     triplets_transformator.reserve(indices_of_wanted_basisvectors.size());
     961             : 
     962           4 :     size_t idx_new = 0;
     963          20 :     for (const auto &idx : indices_of_wanted_basisvectors) {
     964          16 :         if (Eigen::Index(idx) >= basisvectors.cols()) {
     965           0 :             throw std::runtime_error("A basis vector with index " + std::to_string(idx) +
     966             :                                      " could not be found.");
     967             :         }
     968          16 :         triplets_transformator.emplace_back(idx, idx_new++, 1);
     969             :     }
     970             : 
     971           4 :     this->applyRightsideTransformator(triplets_transformator);
     972           4 : }
     973             : 
     974             : template <class Scalar, class State>
     975           4 : void SystemBase<Scalar, State>::applySchriefferWolffTransformation(
     976             :     SystemBase<Scalar, State> &system0) {
     977           4 :     this->diagonalize();
     978           4 :     system0.buildHamiltonian();
     979             : 
     980             :     // Check that system, on which applySchriefferWolffTransformation() is called, is unitary
     981           4 :     if (!this->checkIsUnitary(basisvectors)) {
     982           0 :         throw std::runtime_error("The system, on which applySchriefferWolffTransformation() is "
     983             :                                  "called, is not unitary. Call unitarize() on the system.");
     984             :     }
     985             : 
     986             :     // Check that system0, i.e. the unperturbed system, is unitary
     987           4 :     if (!this->checkIsUnitary(system0.basisvectors)) {
     988           0 :         throw std::runtime_error(
     989             :             "The unperturbed system passed to applySchriefferWolffTransformation() is not "
     990             :             "unitary. Call unitarize() on the system.");
     991             :     }
     992             : 
     993             :     // Check that the unperturbed system is diagonal
     994           4 :     if (!this->checkIsDiagonal(system0.hamiltonian)) {
     995           0 :         throw std::runtime_error("The unperturbed system passed to "
     996             :                                  "applySchriefferWolffTransformation() is not diagonal.");
     997             :     }
     998             : 
     999             :     // --- Express the basis vectors of system0 as a linearcombination of all states ---
    1000             : 
    1001             :     // Combine states and build transformators
    1002           8 :     Eigen::SparseMatrix<Scalar> transformator;
    1003             :     {
    1004           4 :         std::vector<Eigen::Triplet<Scalar>> transformator_triplets;
    1005           4 :         transformator_triplets.reserve(system0.states.size());
    1006             : 
    1007         200 :         for (const auto &entry : system0.states) {
    1008         196 :             auto state_iter = states.template get<1>().find(entry.state);
    1009             : 
    1010             :             // Check that all the states of system0 occur in the perturbed system
    1011         196 :             if (state_iter == states.template get<1>().end()) {
    1012           0 :                 throw std::runtime_error(
    1013             :                     "The unperturbed system contains states that are not occuring. "
    1014             :                     "This might happen if you did not use the same system as basis for the "
    1015             :                     "unperturbed and perturbed system.");
    1016             :             }
    1017         196 :             size_t newidx = state_iter->idx;
    1018             : 
    1019         196 :             transformator_triplets.emplace_back(newidx, entry.idx, 1);
    1020             :         }
    1021             : 
    1022           4 :         transformator.resize(states.size(), system0.states.size());
    1023           4 :         transformator.setFromTriplets(transformator_triplets.begin(), transformator_triplets.end());
    1024             :     }
    1025             : 
    1026             :     // Get basisvectors of system0
    1027           8 :     Eigen::SparseMatrix<Scalar> low_energy_basis0 = transformator * system0.basisvectors;
    1028             : 
    1029             :     // --- Find basisvectors which corresponds to the basis vectors of system0 ---
    1030             : 
    1031             :     // Get overlaps between basis vectors
    1032           8 :     Eigen::VectorX<double> overlap = (basisvectors.adjoint() * low_energy_basis0).cwiseAbs2() *
    1033           4 :         Eigen::VectorX<double>::Ones(low_energy_basis0.cols());
    1034             : 
    1035             :     // Get indices of the low_energy_basis0.cols() largest entries and build transformator
    1036             :     {
    1037           8 :         std::vector<int> indices(basisvectors.cols());
    1038           4 :         std::iota(indices.begin(), indices.end(), 0);
    1039           4 :         std::nth_element(indices.begin(), indices.begin() + low_energy_basis0.cols(), indices.end(),
    1040         368 :                          [&overlap](int a, int b) { return overlap[a] > overlap[b]; });
    1041             : 
    1042           4 :         std::vector<Eigen::Triplet<Scalar>> transformator_triplets;
    1043           4 :         transformator_triplets.reserve(low_energy_basis0.cols());
    1044             : 
    1045          20 :         for (int i = 0; i < low_energy_basis0.cols(); ++i) {
    1046          16 :             transformator_triplets.emplace_back(indices[i], i, 1);
    1047             :         }
    1048             : 
    1049           4 :         transformator.resize(basisvectors.cols(), low_energy_basis0.cols());
    1050           4 :         transformator.setFromTriplets(transformator_triplets.begin(), transformator_triplets.end());
    1051             :     }
    1052             : 
    1053             :     // Get corresponding basis vectors
    1054           8 :     Eigen::SparseMatrix<Scalar> low_energy_basis = basisvectors * transformator;
    1055             : 
    1056             :     // --- Perform the Schrieffer Wolff transformation ---
    1057             : 
    1058             :     // Projector on the selected basis vectors (typically low-energy subspace)
    1059           8 :     Eigen::SparseMatrix<Scalar> projector0 = low_energy_basis0 * low_energy_basis0.adjoint();
    1060           8 :     Eigen::SparseMatrix<Scalar> projector = low_energy_basis * low_energy_basis.adjoint();
    1061             : 
    1062             :     // Reflection operator which change signes of the selectes basis vectors but act
    1063             :     // trivially on others
    1064           8 :     Eigen::MatrixX<Scalar> reflection0 =
    1065           4 :         Eigen::MatrixX<Scalar>::Identity(states.size(), states.size()) - 2 * projector0;
    1066           8 :     Eigen::MatrixX<Scalar> reflection =
    1067           4 :         Eigen::MatrixX<Scalar>::Identity(states.size(), states.size()) - 2 * projector;
    1068             : 
    1069             :     // Direct rotator from low_energy_basis to low_energy_basis0
    1070           8 :     Eigen::SparseMatrix<Scalar> rotator = (reflection0 * reflection).sqrt().sparseView();
    1071             : 
    1072           4 :     if (std::isnan(std::abs(rotator.coeffRef(0, 0)))) {
    1073           0 :         throw std::runtime_error(
    1074             :             "Error in calculating the matrix square root. Try to use the picomplex module or "
    1075             :             "increase accuracy by choosing smaller thresholds.");
    1076             :     }
    1077             : 
    1078             :     // Calculate the effective Hamiltonian
    1079           4 :     transformator = basisvectors.adjoint() * rotator.adjoint() * projector0 * low_energy_basis0;
    1080             : 
    1081           4 :     this->applyRightsideTransformator(transformator);
    1082           4 : }
    1083             : 
    1084             : ////////////////////////////////////////////////////////////////////
    1085             : /// Methods to manipulate entries of the Hamiltonian ///////////////
    1086             : ////////////////////////////////////////////////////////////////////
    1087             : 
    1088             : template <class Scalar, class State>
    1089           7 : size_t SystemBase<Scalar, State>::getStateIndex(const State &searched_state) {
    1090           7 :     this->buildBasis();
    1091             : 
    1092           7 :     if (utils::is_true(searched_state.isGeneralized())) {
    1093           0 :         throw std::runtime_error("The method must not be called on a generalized state.");
    1094             :     }
    1095             : 
    1096           7 :     auto state_iter = states.template get<1>().find(searched_state);
    1097           7 :     if (state_iter == states.template get<1>().end()) {
    1098           0 :         throw std::runtime_error("The method is called on a non-existing state.");
    1099             :     }
    1100             : 
    1101           7 :     return state_iter->idx;
    1102             : }
    1103             : 
    1104             : template <class Scalar, class State>
    1105             : std::vector<size_t>
    1106           7 : SystemBase<Scalar, State>::getStateIndex(const std::vector<State> &searched_states) {
    1107           7 :     this->buildBasis();
    1108             : 
    1109           7 :     std::vector<size_t> indices;
    1110           7 :     indices.reserve(searched_states.size());
    1111             : 
    1112          35 :     for (const auto &state : searched_states) {
    1113          28 :         if (utils::is_true(state.isGeneralized())) {
    1114           0 :             throw std::runtime_error("The method must not be called on a generalized state.");
    1115             :         }
    1116             : 
    1117          28 :         auto state_iter = states.template get<1>().find(state);
    1118          28 :         if (state_iter == states.template get<1>().end()) {
    1119           0 :             throw std::runtime_error("The method is called on a non-existing state.");
    1120             :         }
    1121          28 :         indices.push_back(state_iter->idx);
    1122             :     }
    1123             : 
    1124           7 :     return indices;
    1125             : }
    1126             : 
    1127             : template <class Scalar, class State>
    1128           7 : size_t SystemBase<Scalar, State>::getBasisvectorIndex(const State &searched_state) {
    1129           7 :     this->buildBasis();
    1130             : 
    1131           7 :     Eigen::Index stateidx = this->getStateIndex(searched_state);
    1132             : 
    1133           7 :     double maxval = -1;
    1134           7 :     size_t col_with_maxval = -1;
    1135         365 :     for (int k = 0; k < basisvectors.outerSize(); ++k) { // col == idx_vector
    1136       30414 :         for (typename Eigen::SparseMatrix<Scalar>::InnerIterator triple(basisvectors, k); triple;
    1137       30056 :              ++triple) {
    1138       30298 :             if (triple.row() == stateidx) {
    1139         242 :                 if (std::abs(triple.value()) > maxval) {
    1140          23 :                     col_with_maxval = triple.col();
    1141          23 :                     maxval = std::abs(triple.value());
    1142             :                 }
    1143         242 :                 break;
    1144             :             }
    1145             :         }
    1146             :     }
    1147             : 
    1148           7 :     return col_with_maxval;
    1149             : }
    1150             : 
    1151             : template <class Scalar, class State>
    1152             : std::vector<size_t>
    1153           5 : SystemBase<Scalar, State>::getBasisvectorIndex(const std::vector<State> &searched_states) {
    1154           5 :     this->buildBasis();
    1155             : 
    1156             :     // Ensure that the states within searched_states are unique
    1157             :     {
    1158          10 :         std::set<State> tmp(searched_states.begin(), searched_states.end());
    1159           5 :         if (tmp.size() < searched_states.size()) {
    1160           0 :             throw std::runtime_error("States are occuring multiple times.");
    1161             :         }
    1162             :     }
    1163             : 
    1164             :     // Construct canonical basis vectors
    1165          10 :     Eigen::SparseMatrix<Scalar> canonicalbasis;
    1166             :     {
    1167          10 :         std::vector<size_t> state_indices = this->getStateIndex(searched_states);
    1168           5 :         std::vector<Eigen::Triplet<Scalar>> canonicalbasis_triplets;
    1169           5 :         canonicalbasis_triplets.reserve(searched_states.size());
    1170          25 :         for (size_t i = 0; i < state_indices.size(); ++i) {
    1171          20 :             canonicalbasis_triplets.emplace_back(state_indices[i], i, 1);
    1172             :         }
    1173           5 :         canonicalbasis.resize(states.size(), searched_states.size());
    1174           5 :         canonicalbasis.setFromTriplets(canonicalbasis_triplets.begin(),
    1175          10 :                                        canonicalbasis_triplets.end());
    1176             :     }
    1177             : 
    1178             :     // Get overlaps between basis vectors
    1179          10 :     Eigen::SparseMatrix<double> overlap = (canonicalbasis.adjoint() * basisvectors).cwiseAbs2();
    1180          10 :     Eigen::VectorX<double> overlap_total =
    1181           5 :         Eigen::VectorX<double>::Ones(canonicalbasis.cols()).transpose() * overlap;
    1182             : 
    1183             :     // Get indices of the canonicalbasis.cols() largest entries
    1184          10 :     std::vector<size_t> indices_available(basisvectors.cols());
    1185           5 :     std::iota(indices_available.begin(), indices_available.end(), 0);
    1186           5 :     std::nth_element(indices_available.begin(), indices_available.begin() + canonicalbasis.cols(),
    1187        1260 :                      indices_available.end(), [&overlap_total](size_t a, size_t b) {
    1188         630 :                          return overlap_total[a] > overlap_total[b];
    1189             :                      });
    1190           5 :     indices_available.resize(canonicalbasis.cols());
    1191             : 
    1192             :     // Resort the indices to match the order of searched_states, TODO use Hungarian algorithm
    1193           5 :     std::vector<size_t> indices(canonicalbasis.cols(), std::numeric_limits<size_t>::max());
    1194          25 :     for (const auto &k : indices_available) {
    1195             :         size_t row_with_maxval;
    1196          20 :         double maxval = -1;
    1197          56 :         for (typename Eigen::SparseMatrix<double>::InnerIterator triple(overlap, k); triple;
    1198          36 :              ++triple) {
    1199          64 :             if (indices[triple.row()] == std::numeric_limits<size_t>::max() &&
    1200          28 :                 triple.value() > maxval) {
    1201          22 :                 row_with_maxval = triple.row();
    1202          22 :                 maxval = triple.value();
    1203             :             }
    1204             :         }
    1205             : 
    1206          20 :         if (maxval == -1) {
    1207           0 :             throw std::runtime_error("There is a state for which no basis vector could be found.");
    1208             :         }
    1209             : 
    1210          20 :         indices[row_with_maxval] = k;
    1211             :     }
    1212             : 
    1213          10 :     return indices;
    1214             : }
    1215             : 
    1216             : template <class Scalar, class State>
    1217           0 : void SystemBase<Scalar, State>::forgetStatemixing() {
    1218           0 :     this->diagonalize();
    1219             : 
    1220           0 :     std::vector<Eigen::Triplet<Scalar>> basisvectors_triplets;
    1221           0 :     basisvectors_triplets.reserve(basisvectors.cols());
    1222           0 :     double threshold = std::sqrt(0.5);
    1223             : 
    1224           0 :     for (int k = 0; k < basisvectors.outerSize(); ++k) { // col == idx_vector
    1225           0 :         for (typename Eigen::SparseMatrix<Scalar>::InnerIterator triple(basisvectors, k); triple;
    1226           0 :              ++triple) {
    1227           0 :             if (std::abs(triple.value()) > threshold) {
    1228           0 :                 basisvectors_triplets.emplace_back(triple.row(), triple.col(), 1);
    1229           0 :                 break;
    1230             :             }
    1231             :         }
    1232             :     }
    1233             : 
    1234           0 :     if (Eigen::Index(basisvectors_triplets.size()) < basisvectors.cols()) {
    1235           0 :         throw std::runtime_error(
    1236             :             "The states are mixed too strongly for calling forgetStatemixing().");
    1237             :     }
    1238             : 
    1239           0 :     basisvectors.setFromTriplets(basisvectors_triplets.begin(), basisvectors_triplets.end());
    1240           0 : }
    1241             : 
    1242             : template <class Scalar, class State>
    1243           0 : Scalar SystemBase<Scalar, State>::getHamiltonianEntry(const State &state_row,
    1244             :                                                       const State &state_col) {
    1245           0 :     this->buildHamiltonian();
    1246             : 
    1247           0 :     size_t idx_row = this->getStateIndex(state_row);
    1248           0 :     size_t idx_col = this->getStateIndex(state_col);
    1249             : 
    1250           0 :     return (basisvectors.row(idx_row) * hamiltonian).dot(basisvectors.row(idx_col));
    1251             : }
    1252             : 
    1253             : template <class Scalar, class State>
    1254           0 : void SystemBase<Scalar, State>::setHamiltonianEntry(const State &state_row, const State &state_col,
    1255             :                                                     Scalar value) {
    1256           0 :     this->buildHamiltonian();
    1257             : 
    1258           0 :     size_t idx_row = this->getStateIndex(state_row);
    1259           0 :     size_t idx_col = this->getStateIndex(state_col);
    1260             : 
    1261           0 :     value -= (basisvectors.row(idx_row) * hamiltonian).dot(basisvectors.row(idx_col));
    1262             : 
    1263           0 :     Eigen::SparseMatrix<Scalar> tmp(states.size(), states.size());
    1264           0 :     tmp.reserve(2);
    1265           0 :     tmp.insert(idx_row, idx_col) = value;
    1266           0 :     if (idx_row != idx_col) {
    1267           0 :         tmp.insert(idx_col, idx_row) = utils::conjugate(value);
    1268             :     }
    1269           0 :     tmp.makeCompressed();
    1270             : 
    1271           0 :     hamiltonian += basisvectors.adjoint() * tmp * basisvectors;
    1272           0 : }
    1273             : 
    1274             : template <class Scalar, class State>
    1275           0 : void SystemBase<Scalar, State>::addHamiltonianEntry(const State &state_row, const State &state_col,
    1276             :                                                     Scalar value) {
    1277           0 :     this->buildHamiltonian();
    1278             : 
    1279           0 :     size_t idx_row = this->getStateIndex(state_row);
    1280           0 :     size_t idx_col = this->getStateIndex(state_col);
    1281             : 
    1282           0 :     Eigen::SparseMatrix<Scalar> tmp(states.size(), states.size());
    1283           0 :     tmp.reserve(2);
    1284           0 :     tmp.insert(idx_row, idx_col) = value;
    1285           0 :     if (idx_row != idx_col) {
    1286           0 :         tmp.insert(idx_col, idx_row) = utils::conjugate(value);
    1287             :     }
    1288           0 :     tmp.makeCompressed();
    1289             : 
    1290           0 :     hamiltonian += basisvectors.adjoint() * tmp * basisvectors;
    1291           0 : }
    1292             : 
    1293             : template <class Scalar, class State>
    1294           0 : SystemBase<Scalar, State>::SystemBase()
    1295           0 :     : energy_min(std::numeric_limits<double>::lowest()),
    1296           0 :       energy_max(std::numeric_limits<double>::max()) {}
    1297             : 
    1298             : template <class Scalar, class State>
    1299         121 : SystemBase<Scalar, State>::SystemBase(MatrixElementCache &cache)
    1300         242 :     : m_cache(std::addressof(cache)), energy_min(std::numeric_limits<double>::lowest()),
    1301         121 :       energy_max(std::numeric_limits<double>::max()) {}
    1302             : 
    1303             : template <class Scalar, class State>
    1304           0 : SystemBase<Scalar, State>::SystemBase(MatrixElementCache &cache, bool memory_saving)
    1305           0 :     : m_cache(std::addressof(cache)), energy_min(std::numeric_limits<double>::lowest()),
    1306           0 :       energy_max(std::numeric_limits<double>::max()), memory_saving(memory_saving) {}
    1307             : 
    1308             : ////////////////////////////////////////////////////////////////////
    1309             : /// Helper method that shoul be called by the derived classes //////
    1310             : ////////////////////////////////////////////////////////////////////
    1311             : 
    1312             : template <class Scalar, class State>
    1313         435 : void SystemBase<Scalar, State>::onParameterChange() {
    1314             :     // Check variables for consistency
    1315         435 :     if ((basisvectors_unperturbed_cache.size() == 0) !=
    1316         435 :         (hamiltonian_unperturbed_cache.size() == 0)) {
    1317           0 :         throw std::runtime_error("Inconsistent variables at " + std::string(__FILE__) + ":" +
    1318           0 :                                  std::to_string(__LINE__) + ".");
    1319             :     }
    1320             : 
    1321             :     // Throw error if the Hamiltonian cannot be changed anymore
    1322         435 :     if (is_interaction_already_contained && basisvectors_unperturbed_cache.size() == 0) {
    1323           0 :         throw std::runtime_error(
    1324             :             "If memory saving is activated or unitarize() has been called, one cannot change "
    1325             :             "parameters after interaction was added to the Hamiltonian.");
    1326             :     }
    1327             : 
    1328         435 :     is_new_hamiltonian_required = true;
    1329         435 : }
    1330             : 
    1331             : template <class Scalar, class State>
    1332          83 : void SystemBase<Scalar, State>::onSymmetryChange() {
    1333             :     // Throw error if the Symmetry cannot be changed anymore
    1334          83 :     if (!states.empty()) {
    1335           0 :         throw std::runtime_error("One cannot change symmetries after the basis was built.");
    1336             :     }
    1337          83 : }
    1338             : 
    1339             : ////////////////////////////////////////////////////////////////////
    1340             : /// Helper methods to check diagonality and unitarity of a matrix //
    1341             : ////////////////////////////////////////////////////////////////////
    1342             : 
    1343             : template <class Scalar, class State>
    1344         483 : bool SystemBase<Scalar, State>::checkIsDiagonal(const Eigen::SparseMatrix<Scalar> &mat) {
    1345         966 :     Eigen::SparseMatrix<Scalar> tmp = mat;
    1346         483 :     tmp.prune(1e-12, 1);
    1347             : 
    1348        6832 :     for (int k = 0; k < tmp.outerSize(); ++k) {
    1349       13368 :         for (typename Eigen::SparseMatrix<Scalar>::InnerIterator triple(tmp, k); triple; ++triple) {
    1350        7019 :             if (triple.row() != triple.col()) {
    1351         335 :                 return false;
    1352             :             }
    1353             :         }
    1354             :     }
    1355         148 :     return true;
    1356             : }
    1357             : 
    1358             : template <class Scalar, class State>
    1359           8 : bool SystemBase<Scalar, State>::checkIsUnitary(const Eigen::SparseMatrix<Scalar> &mat) {
    1360          16 :     Eigen::SparseMatrix<Scalar> tmp = (mat.adjoint() * mat).pruned(1e-12, 1);
    1361             : 
    1362           8 :     if (tmp.nonZeros() != tmp.outerSize()) {
    1363           0 :         return false;
    1364             :     }
    1365             : 
    1366         240 :     for (int k = 0; k < tmp.outerSize(); ++k) {
    1367         464 :         for (typename Eigen::SparseMatrix<Scalar>::InnerIterator triple(tmp, k); triple; ++triple) {
    1368         232 :             if (triple.row() != triple.col()) {
    1369           0 :                 return false;
    1370             :             }
    1371         464 :             if (std::abs(std::real(triple.value()) - 1) > 1e-12 ||
    1372         232 :                 std::abs(std::imag(triple.value())) > 1e-12) {
    1373           0 :                 return false;
    1374             :             }
    1375             :         }
    1376             :     }
    1377           8 :     return true;
    1378             : }
    1379             : 
    1380             : ////////////////////////////////////////////////////////////////////
    1381             : /// Helper methods to check the validity of states /////////////////
    1382             : ////////////////////////////////////////////////////////////////////
    1383             : 
    1384             : template <class Scalar, class State>
    1385           0 : bool SystemBase<Scalar, State>::checkIsQuantumstateValid(const State &state) {
    1386           0 :     return checkIsQuantumstateValid(state, state.isArtificial());
    1387             : }
    1388             : 
    1389             : template <class Scalar, class State>
    1390           0 : bool SystemBase<Scalar, State>::checkIsQuantumstateValid(const StateOne &state, bool a) {
    1391           0 :     return a ||
    1392           0 :         (checkIsQuantumnumberValid(state.getN(), range_n) &&
    1393           0 :          checkIsQuantumnumberValid(state.getL(), range_l) &&
    1394           0 :          checkIsQuantumnumberValid(state.getJ(), range_j) &&
    1395           0 :          checkIsQuantumnumberValid(state.getM(), range_m));
    1396             : }
    1397             : 
    1398             : template <class Scalar, class State>
    1399           0 : bool SystemBase<Scalar, State>::checkIsQuantumstateValid(const StateTwo &state,
    1400             :                                                          std::array<bool, 2> a) {
    1401           0 :     bool valid = true;
    1402           0 :     for (int idx = 0; idx < 2; ++idx) {
    1403           0 :         valid = valid &&
    1404           0 :             (a[idx] ||
    1405           0 :              (checkIsQuantumnumberValid(state.getN(idx), range_n) &&
    1406           0 :               checkIsQuantumnumberValid(state.getL(idx), range_l) &&
    1407           0 :               checkIsQuantumnumberValid(state.getJ(idx), range_j) &&
    1408           0 :               checkIsQuantumnumberValid(state.getM(idx), range_m)));
    1409             :     }
    1410           0 :     return valid;
    1411             : }
    1412             : 
    1413             : template <class Scalar, class State>
    1414      155696 : bool SystemBase<Scalar, State>::checkIsEnergyValid(double e) {
    1415      265643 :     return (e > energy_min || energy_min == std::numeric_limits<double_t>::lowest()) &&
    1416      207247 :         (e < energy_max ||
    1417       97300 :          energy_max ==
    1418      252996 :              std::numeric_limits<double_t>::max()); // TODO take into account numerical errors
    1419             : }
    1420             : 
    1421             : ////////////////////////////////////////////////////////////////////
    1422             : /// Helper methods to change the set of basis vectors //////////////
    1423             : ////////////////////////////////////////////////////////////////////
    1424             : 
    1425             : template <class Scalar, class State>
    1426          75 : void SystemBase<Scalar, State>::applyLeftsideTransformator(
    1427             :     std::vector<Eigen::Triplet<Scalar>> &triplets_transformator) {
    1428         150 :     Eigen::SparseMatrix<Scalar> transformator(triplets_transformator.size(), basisvectors.rows());
    1429          75 :     transformator.setFromTriplets(triplets_transformator.begin(), triplets_transformator.end());
    1430             : 
    1431          75 :     this->applyLeftsideTransformator(transformator);
    1432          75 : }
    1433             : 
    1434             : template <class Scalar, class State>
    1435          75 : void SystemBase<Scalar, State>::applyLeftsideTransformator(
    1436             :     Eigen::SparseMatrix<Scalar> &transformator) {
    1437             :     // Apply transformator in order to remove rows from the basisvector matrix (i.e. states)
    1438          75 :     basisvectors = transformator * basisvectors;
    1439          75 :     if (basisvectors_unperturbed_cache.size() != 0) {
    1440           0 :         basisvectors_unperturbed_cache = transformator * basisvectors_unperturbed_cache;
    1441             :     }
    1442          75 : }
    1443             : 
    1444             : template <class Scalar, class State>
    1445          79 : void SystemBase<Scalar, State>::applyRightsideTransformator(
    1446             :     std::vector<Eigen::Triplet<Scalar>> &triplets_transformator) {
    1447         158 :     Eigen::SparseMatrix<Scalar> transformator(basisvectors.cols(), triplets_transformator.size());
    1448          79 :     transformator.setFromTriplets(triplets_transformator.begin(), triplets_transformator.end());
    1449             : 
    1450          79 :     this->applyRightsideTransformator(transformator);
    1451          79 : }
    1452             : 
    1453             : template <class Scalar, class State>
    1454          83 : void SystemBase<Scalar, State>::applyRightsideTransformator(
    1455             :     Eigen::SparseMatrix<Scalar> &transformator) {
    1456             :     // Apply transformator in order to remove columns from the basisvector matrix (i.e. basis
    1457             :     // vectors)
    1458          83 :     basisvectors = basisvectors * transformator;
    1459          83 :     if (basisvectors_unperturbed_cache.size() != 0) {
    1460           3 :         basisvectors_unperturbed_cache = basisvectors_unperturbed_cache * transformator;
    1461             :     }
    1462             : 
    1463             :     // Apply transformator in order to remove rows and columns from the matrices that help
    1464             :     // constructing the total Hamiltonian
    1465          83 :     this->transformInteraction(transformator);
    1466             : 
    1467             :     // Apply transformator in order to remove rows and columns from the total Hamiltonian
    1468          83 :     hamiltonian = transformator.adjoint() * hamiltonian * transformator;
    1469          83 :     if (hamiltonian_unperturbed_cache.size() != 0) {
    1470           3 :         hamiltonian_unperturbed_cache =
    1471           6 :             transformator.adjoint() * hamiltonian_unperturbed_cache * transformator;
    1472             :     }
    1473          83 : }
    1474             : 
    1475             : template <class Scalar, class State>
    1476          75 : void SystemBase<Scalar, State>::removeRestrictedStates(
    1477             :     std::function<bool(const enumerated_state<State> &)> checkIsValidEntry) {
    1478             :     // Build transformator and remove states
    1479         150 :     typename states_set<State>::type states_new;
    1480          75 :     states_new.reserve(states.size());
    1481         150 :     std::vector<Eigen::Triplet<Scalar>> triplets_transformator;
    1482          75 :     triplets_transformator.reserve(states.size());
    1483             : 
    1484          75 :     size_t idx_new = 0;
    1485             : 
    1486       29999 :     for (const auto &entry : states) {
    1487       14962 :         if (checkIsValidEntry(entry)) {
    1488        8284 :             states_new.push_back(enumerated_state<State>(idx_new, entry.state));
    1489        8284 :             triplets_transformator.emplace_back(idx_new, entry.idx, 1);
    1490        8284 :             ++idx_new;
    1491             :         }
    1492             :     }
    1493             : 
    1494          75 :     states_new.shrink_to_fit();
    1495          75 :     states = states_new;
    1496             : 
    1497          75 :     this->applyLeftsideTransformator(triplets_transformator);
    1498          75 : }
    1499             : 
    1500             : ////////////////////////////////////////////////////////////////////
    1501             : /// Utility methods ////////////////////////////////////////////////
    1502             : ////////////////////////////////////////////////////////////////////
    1503             : 
    1504             : template <class Scalar, class State>
    1505             : Eigen::Matrix<double, 3, 3>
    1506           1 : SystemBase<Scalar, State>::buildRotator(std::array<double, 3> to_z_axis,
    1507             :                                         std::array<double, 3> to_y_axis) {
    1508           1 :     auto to_z_axis_mapped = Eigen::Map<Eigen::Matrix<double, 3, 1>>(&to_z_axis[0]).normalized();
    1509           1 :     auto to_y_axis_mapped = Eigen::Map<Eigen::Matrix<double, 3, 1>>(&to_y_axis[0]).normalized();
    1510             : 
    1511           1 :     double tolerance = 1e-16;
    1512           1 :     if (std::abs(to_z_axis_mapped.dot(to_y_axis_mapped)) > tolerance) {
    1513           0 :         throw std::runtime_error("The z-axis and the y-axis are not orhogonal.");
    1514             :     }
    1515             : 
    1516           1 :     Eigen::Matrix<double, 3, 3> transformator;
    1517           1 :     transformator << to_y_axis_mapped.cross(to_z_axis_mapped), to_y_axis_mapped, to_z_axis_mapped;
    1518             : 
    1519           2 :     return transformator;
    1520             : }
    1521             : 
    1522             : template <class Scalar, class State>
    1523           3 : Eigen::Matrix<double, 3, 3> SystemBase<Scalar, State>::buildRotator(const double &alpha,
    1524             :                                                                     const double &beta,
    1525             :                                                                     const double &gamma) {
    1526           3 :     Eigen::Matrix<double, 3, 3> transformator;
    1527             : 
    1528           6 :     transformator = Eigen::AngleAxisd(alpha, Eigen::Matrix<double, 3, 1>::UnitZ()) *
    1529           6 :         Eigen::AngleAxisd(beta, Eigen::Matrix<double, 3, 1>::UnitY()) *
    1530           3 :         Eigen::AngleAxisd(gamma, Eigen::Matrix<double, 3, 1>::UnitZ());
    1531             : 
    1532           3 :     return transformator;
    1533             : }
    1534             : 
    1535             : template <class Scalar, class State>
    1536             : Eigen::Matrix<double, 3, 1>
    1537           1 : SystemBase<Scalar, State>::getEulerAngles(const std::array<double, 3> &to_z_axis,
    1538             :                                           const std::array<double, 3> &to_y_axis) {
    1539           1 :     Eigen::Matrix<double, 3, 3> rotator = this->buildRotator(to_z_axis, to_y_axis);
    1540           1 :     Eigen::Matrix<double, 3, 1> euler_zyz = rotator.eulerAngles(2, 1, 2);
    1541           2 :     return euler_zyz; // alpha, beta, gamma
    1542             : }
    1543             : 
    1544             : template <class Scalar, class State>
    1545         164 : void SystemBase<Scalar, State>::forgetRestrictions() {
    1546         164 :     energy_min = std::numeric_limits<double>::lowest();
    1547         164 :     energy_max = std::numeric_limits<double>::max();
    1548         164 :     range_n.clear();
    1549         164 :     range_l.clear();
    1550         164 :     range_j.clear();
    1551         164 :     range_m.clear();
    1552         164 :     states_to_add.clear();
    1553         164 : }
    1554             : 
    1555             : ////////////////////////////////////////////////////////////////////
    1556             : /// Method to update the system ////////////////////////////////////
    1557             : ////////////////////////////////////////////////////////////////////
    1558             : 
    1559             : template <class Scalar, class State>
    1560           0 : void SystemBase<Scalar, State>::updateEverything() {
    1561             : 
    1562           0 :     if (!range_n.empty() || !range_l.empty() || !range_j.empty() || !range_m.empty()) {
    1563             : 
    1564             :         ////////////////////////////////////////////////////////////////////
    1565             :         /// Remove restricted states ///////////////////////////////////////
    1566             :         ////////////////////////////////////////////////////////////////////
    1567             : 
    1568             :         // Remove states if the quantum numbers are not allowed
    1569           0 :         removeRestrictedStates([=](const enumerated_state<State> &entry) -> bool {
    1570           0 :             return this->checkIsQuantumstateValid(entry.state);
    1571             :         });
    1572             : 
    1573             :         // Comunicate that the list of states has changed
    1574           0 :         this->onStatesChange();
    1575             :     }
    1576             : 
    1577           0 :     if (!range_n.empty() || !range_l.empty() || !range_j.empty() || !range_m.empty() ||
    1578           0 :         energy_min != std::numeric_limits<double>::lowest() ||
    1579           0 :         energy_max != std::numeric_limits<double>::max()) {
    1580             : 
    1581             :         ////////////////////////////////////////////////////////////////////
    1582             :         /// Remove vectors with restricted energies or too small norm //////
    1583             :         ////////////////////////////////////////////////////////////////////
    1584             : 
    1585             :         // Build transformator and remove vectors (if their energy is not allowed or the squared
    1586             :         // norm too small)
    1587           0 :         std::vector<Eigen::Triplet<Scalar>> triplets_transformator;
    1588           0 :         triplets_transformator.reserve(basisvectors.cols());
    1589             : 
    1590           0 :         size_t idx_new = 0;
    1591           0 :         for (int idx = 0; idx < basisvectors.cols(); ++idx) { // idx = col = num basis vector
    1592             : 
    1593           0 :             if (checkIsEnergyValid(std::real(hamiltonian.coeff(idx, idx)))) {
    1594           0 :                 double_t sqnorm = 0;
    1595             : 
    1596             :                 // Calculate the square norm of the columns of the basisvector matrix
    1597           0 :                 for (typename Eigen::SparseMatrix<Scalar>::InnerIterator triple(basisvectors, idx);
    1598           0 :                      triple; ++triple) {
    1599           0 :                     sqnorm += std::pow(std::abs(triple.value()), 2);
    1600             :                 }
    1601           0 :                 if (sqnorm > threshold_for_sqnorm) {
    1602           0 :                     triplets_transformator.emplace_back(idx, idx_new++, 1);
    1603             :                 }
    1604             :             }
    1605             :         }
    1606             : 
    1607           0 :         this->applyRightsideTransformator(triplets_transformator);
    1608             : 
    1609             :         ////////////////////////////////////////////////////////////////////
    1610             :         /// Remove states that barely occur within the vectors /////////////
    1611             :         ////////////////////////////////////////////////////////////////////
    1612             : 
    1613             :         // Calculate the square norm of the rows of the basisvector matrix
    1614           0 :         std::vector<double> sqnorm_list(basisvectors.rows(), 0);
    1615           0 :         for (int k = 0; k < this->basisvectors.cols(); ++k) {
    1616           0 :             for (typename Eigen::SparseMatrix<Scalar>::InnerIterator triple(basisvectors, k);
    1617           0 :                  triple; ++triple) {
    1618           0 :                 sqnorm_list[triple.row()] += std::pow(std::abs(triple.value()), 2);
    1619             :             }
    1620             :         }
    1621             : 
    1622             :         // Remove states if the squared norm is to small
    1623           0 :         removeRestrictedStates([=](const enumerated_state<State> &entry) -> bool {
    1624           0 :             return sqnorm_list[entry.idx] > threshold_for_sqnorm;
    1625             :         });
    1626             : 
    1627             :         // Comunicate that the list of states has changed
    1628           0 :         this->onStatesChange();
    1629             :     }
    1630             : 
    1631           0 :     if (!states_to_add.empty()) {
    1632           0 :         throw std::runtime_error(
    1633             :             "States cannot be added anymore once the basis vectors have been created.");
    1634             :     }
    1635             : 
    1636             :     // Forget basis restrictions as they were applied now
    1637           0 :     this->forgetRestrictions();
    1638           0 : }
    1639             : 
    1640             : template class SystemBase<std::complex<double>, StateOne>;
    1641             : template class SystemBase<std::complex<double>, StateTwo>;
    1642             : template class SystemBase<double, StateOne>;
    1643             : template class SystemBase<double, StateTwo>;

Generated by: LCOV version 1.14