LCOV - code coverage report
Current view: top level - src/basis - BasisPair.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 96 104 92.3 %
Date: 2025-05-02 21:49:55 Functions: 13 32 40.6 %

          Line data    Source code
       1             : // SPDX-FileCopyrightText: 2024 Pairinteraction Developers
       2             : // SPDX-License-Identifier: LGPL-3.0-or-later
       3             : 
       4             : #include "pairinteraction/basis/BasisPair.hpp"
       5             : 
       6             : #include "pairinteraction/basis/BasisAtom.hpp"
       7             : #include "pairinteraction/basis/BasisPairCreator.hpp"
       8             : #include "pairinteraction/database/Database.hpp"
       9             : #include "pairinteraction/ket/KetAtom.hpp"
      10             : #include "pairinteraction/ket/KetPair.hpp"
      11             : #include "pairinteraction/system/SystemAtom.hpp"
      12             : #include "pairinteraction/utils/Range.hpp"
      13             : #include "pairinteraction/utils/tensor.hpp"
      14             : 
      15             : #include <memory>
      16             : #include <oneapi/tbb.h>
      17             : #include <vector>
      18             : 
      19             : namespace pairinteraction {
      20             : template <typename Scalar>
      21          22 : BasisPair<Scalar>::BasisPair(Private /*unused*/, ketvec_t &&kets,
      22             :                              map_range_t &&map_range_of_state_index2,
      23             :                              map_indices_t &&state_indices_to_ket_index,
      24             :                              std::shared_ptr<const BasisAtom<Scalar>> basis1,
      25             :                              std::shared_ptr<const BasisAtom<Scalar>> basis2)
      26          22 :     : Basis<BasisPair<Scalar>>(std::move(kets)),
      27          22 :       map_range_of_state_index2(std::move(map_range_of_state_index2)),
      28          22 :       state_indices_to_ket_index(std::move(state_indices_to_ket_index)), basis1(std::move(basis1)),
      29          44 :       basis2(std::move(basis2)) {}
      30             : 
      31             : template <typename Scalar>
      32             : const typename BasisPair<Scalar>::range_t &
      33     1175698 : BasisPair<Scalar>::get_index_range(size_t state_index1) const {
      34     1175698 :     return map_range_of_state_index2.at(state_index1);
      35             : }
      36             : 
      37             : template <typename Scalar>
      38         137 : std::shared_ptr<const BasisAtom<Scalar>> BasisPair<Scalar>::get_basis1() const {
      39         137 :     return basis1;
      40             : }
      41             : 
      42             : template <typename Scalar>
      43         137 : std::shared_ptr<const BasisAtom<Scalar>> BasisPair<Scalar>::get_basis2() const {
      44         137 :     return basis2;
      45             : }
      46             : 
      47             : template <typename Scalar>
      48     1549856 : int BasisPair<Scalar>::get_ket_index_from_tuple(size_t state_index1, size_t state_index2) const {
      49     1549856 :     if (state_indices_to_ket_index.count({state_index1, state_index2}) == 0) {
      50      278199 :         return -1;
      51             :     }
      52     1271657 :     return state_indices_to_ket_index.at({state_index1, state_index2});
      53             : }
      54             : 
      55             : template <typename Scalar>
      56             : Eigen::VectorX<Scalar>
      57          13 : BasisPair<Scalar>::get_amplitudes(std::shared_ptr<const KetAtom> ket1,
      58             :                                   std::shared_ptr<const KetAtom> ket2) const {
      59          13 :     return get_amplitudes(basis1->get_canonical_state_from_ket(ket1),
      60          13 :                           basis2->get_canonical_state_from_ket(ket2))
      61          39 :         .transpose();
      62             : }
      63             : 
      64             : template <typename Scalar>
      65             : Eigen::SparseMatrix<Scalar, Eigen::RowMajor>
      66          13 : BasisPair<Scalar>::get_amplitudes(std::shared_ptr<const BasisAtom<Scalar>> other1,
      67             :                                   std::shared_ptr<const BasisAtom<Scalar>> other2) const {
      68          26 :     if (other1->get_id_of_kets() != basis1->get_id_of_kets() ||
      69          13 :         other2->get_id_of_kets() != basis2->get_id_of_kets()) {
      70           0 :         throw std::invalid_argument("The other objects must be expressed using the same kets.");
      71             :     }
      72             : 
      73          13 :     constexpr real_t numerical_precision = 100 * std::numeric_limits<real_t>::epsilon();
      74             : 
      75          13 :     Eigen::SparseMatrix<Scalar, Eigen::RowMajor> coefficients1 =
      76          13 :         basis1->get_coefficients().adjoint() * other1->get_coefficients();
      77          13 :     Eigen::SparseMatrix<Scalar, Eigen::RowMajor> coefficients2 =
      78          13 :         basis2->get_coefficients().adjoint() * other2->get_coefficients();
      79             : 
      80          13 :     oneapi::tbb::concurrent_vector<Eigen::Triplet<Scalar>> triplets;
      81             : 
      82             :     // Loop over the rows of the first coefficient matrix
      83          13 :     oneapi::tbb::parallel_for(
      84          13 :         oneapi::tbb::blocked_range<Eigen::Index>(0, coefficients1.outerSize()),
      85         759 :         [&](const auto &range) {
      86        1492 :             for (Eigen::Index row1 = range.begin(); row1 != range.end(); ++row1) {
      87             : 
      88         746 :                 const auto &range_row2 = this->get_index_range(row1);
      89             : 
      90             :                 // Loop over the rows of the second coefficient matrix that are energetically
      91             :                 // allowed
      92        9260 :                 for (auto row2 = static_cast<Eigen::Index>(range_row2.min());
      93        9260 :                      row2 < static_cast<Eigen::Index>(range_row2.max()); ++row2) {
      94             : 
      95        8514 :                     Eigen::Index row = get_ket_index_from_tuple(row1, row2);
      96        8514 :                     if (row < 0) {
      97         384 :                         continue;
      98             :                     }
      99             : 
     100             :                     // Loop over the non-zero column elements of the first coefficient matrix
     101        8390 :                     for (typename Eigen::SparseMatrix<Scalar, Eigen::RowMajor>::InnerIterator it1(
     102             :                              coefficients1, row1);
     103        8390 :                          it1; ++it1) {
     104             : 
     105         260 :                         Eigen::Index col1 = it1.col();
     106         260 :                         Scalar value1 = it1.value();
     107             : 
     108             :                         // Loop over the non-zero column elements of the second coefficient matrix
     109         284 :                         for (typename Eigen::SparseMatrix<Scalar, Eigen::RowMajor>::InnerIterator
     110         260 :                                  it2(coefficients2, row2);
     111         284 :                              it2; ++it2) {
     112             : 
     113          24 :                             Eigen::Index col2 = it2.col();
     114          24 :                             Scalar value2 = it2.value();
     115          24 :                             Eigen::Index col = col1 * coefficients2.cols() + col2;
     116             : 
     117             :                             // Store the entry
     118           0 :                             Scalar value = value1 * value2;
     119          24 :                             if (std::abs(value) > numerical_precision) {
     120          24 :                                 triplets.emplace_back(row, col, value);
     121             :                             }
     122             :                         }
     123             :                     }
     124             :                 }
     125             :             }
     126             :         });
     127             : 
     128             :     // Construct the combined matrix from the triplets
     129          13 :     Eigen::SparseMatrix<Scalar, Eigen::RowMajor> matrix(this->get_number_of_kets(),
     130          13 :                                                         other1->get_number_of_states() *
     131          13 :                                                             other2->get_number_of_states());
     132          13 :     matrix.setFromTriplets(triplets.begin(), triplets.end());
     133          13 :     matrix.makeCompressed();
     134             : 
     135          26 :     return matrix.adjoint() * this->get_coefficients();
     136          13 : }
     137             : 
     138             : template <typename Scalar>
     139             : Eigen::VectorX<typename BasisPair<Scalar>::real_t>
     140          13 : BasisPair<Scalar>::get_overlaps(std::shared_ptr<const KetAtom> ket1,
     141             :                                 std::shared_ptr<const KetAtom> ket2) const {
     142          13 :     return get_amplitudes(ket1, ket2).cwiseAbs2();
     143             : }
     144             : 
     145             : template <typename Scalar>
     146             : Eigen::SparseMatrix<typename BasisPair<Scalar>::real_t, Eigen::RowMajor>
     147           0 : BasisPair<Scalar>::get_overlaps(std::shared_ptr<const BasisAtom<Scalar>> other1,
     148             :                                 std::shared_ptr<const BasisAtom<Scalar>> other2) const {
     149           0 :     return get_amplitudes(other1, other2).cwiseAbs2();
     150             : }
     151             : 
     152             : template <typename Scalar>
     153           0 : Eigen::VectorX<Scalar> BasisPair<Scalar>::get_matrix_elements(std::shared_ptr<const ket_t> /*ket*/,
     154             :                                                               OperatorType /*type*/,
     155             :                                                               int /*q*/) const {
     156           0 :     throw std::invalid_argument("It is required to specify one operator for each atom.");
     157             : }
     158             : 
     159             : template <typename Scalar>
     160             : Eigen::SparseMatrix<Scalar, Eigen::RowMajor>
     161           0 : BasisPair<Scalar>::get_matrix_elements(std::shared_ptr<const Type> /*final*/, OperatorType /*type*/,
     162             :                                        int /*q*/) const {
     163           0 :     throw std::invalid_argument("It is required to specify one operator for each atom.");
     164             : }
     165             : 
     166             : template <typename Scalar>
     167             : Eigen::SparseMatrix<Scalar, Eigen::RowMajor>
     168          11 : BasisPair<Scalar>::get_matrix_elements(std::shared_ptr<const Type> final, OperatorType type1,
     169             :                                        OperatorType type2, int q1, int q2) const {
     170          11 :     auto initial1 = this->get_basis1();
     171          11 :     auto initial2 = this->get_basis2();
     172          11 :     auto final1 = final->get_basis1();
     173          11 :     auto final2 = final->get_basis2();
     174             : 
     175          11 :     auto matrix_elements1 =
     176          11 :         initial1->get_database().get_matrix_elements(initial1, final1, type1, q1);
     177          11 :     auto matrix_elements2 =
     178          11 :         initial2->get_database().get_matrix_elements(initial2, final2, type2, q2);
     179          11 :     auto matrix_elements = utils::calculate_tensor_product(this->shared_from_this(), final,
     180             :                                                            matrix_elements1, matrix_elements2);
     181          11 :     assert(static_cast<size_t>(matrix_elements.rows()) == final->get_number_of_kets());
     182          11 :     assert(static_cast<size_t>(matrix_elements.cols()) == this->get_number_of_kets());
     183             : 
     184          22 :     return final->get_coefficients().adjoint() * matrix_elements * this->get_coefficients();
     185          11 : }
     186             : 
     187             : template <typename Scalar>
     188             : Eigen::SparseMatrix<Scalar, Eigen::RowMajor>
     189           1 : BasisPair<Scalar>::get_matrix_elements(std::shared_ptr<const BasisAtom<Scalar>> final1,
     190             :                                        std::shared_ptr<const BasisAtom<Scalar>> final2,
     191             :                                        OperatorType type1, OperatorType type2, int q1,
     192             :                                        int q2) const {
     193             :     // Construct a pair basis with the two single-atom bases
     194           1 :     auto system1 = SystemAtom<Scalar>(final1);
     195           1 :     auto system2 = SystemAtom<Scalar>(final2);
     196           1 :     auto final = BasisPairCreator<Scalar>().add(system1).add(system2).create();
     197           1 :     assert(final->get_number_of_states() ==
     198             :            final1->get_number_of_states() * final2->get_number_of_states());
     199           1 :     assert(final->get_number_of_kets() ==
     200             :            final1->get_number_of_states() * final2->get_number_of_states());
     201             : 
     202           2 :     return this->get_matrix_elements(final, type1, type2, q1, q2);
     203           1 : }
     204             : 
     205             : template <typename Scalar>
     206             : Eigen::VectorX<Scalar>
     207           1 : BasisPair<Scalar>::get_matrix_elements(std::shared_ptr<const ket_t> ket, OperatorType type1,
     208             :                                        OperatorType type2, int q1, int q2) const {
     209             :     // Construct a pair basis containing only the pair ket
     210           1 :     auto final = this->get_canonical_state_from_ket(ket);
     211           1 :     assert(final->get_number_of_states() == 1);
     212             : 
     213           3 :     return this->get_matrix_elements(final, type1, type2, q1, q2).row(0);
     214           1 : }
     215             : 
     216             : template <typename Scalar>
     217             : Eigen::VectorX<Scalar>
     218           1 : BasisPair<Scalar>::get_matrix_elements(std::shared_ptr<const KetAtom> ket1,
     219             :                                        std::shared_ptr<const KetAtom> ket2, OperatorType type1,
     220             :                                        OperatorType type2, int q1, int q2) const {
     221             :     // Construct a pair basis with the two single-atom kets
     222           1 :     auto final1 = this->get_basis1()->get_canonical_state_from_ket(ket1);
     223           1 :     auto final2 = this->get_basis2()->get_canonical_state_from_ket(ket2);
     224           1 :     auto system1 = SystemAtom<Scalar>(final1);
     225           1 :     auto system2 = SystemAtom<Scalar>(final2);
     226           1 :     auto final = BasisPairCreator<Scalar>().add(system1).add(system2).create();
     227           1 :     assert(final->get_number_of_states() == 1);
     228           1 :     assert(final->get_number_of_kets() == 1);
     229             : 
     230           3 :     return this->get_matrix_elements(final, type1, type2, q1, q2).row(0);
     231           1 : }
     232             : 
     233             : // Explicit instantiations
     234             : template class BasisPair<double>;
     235             : template class BasisPair<std::complex<double>>;
     236             : } // namespace pairinteraction

Generated by: LCOV version 1.16