pairinteraction
A Rydberg Interaction Calculator
BasisPairCreator.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2024 Pairinteraction Developers
2// SPDX-License-Identifier: LGPL-3.0-or-later
3
5
13
14#include <algorithm>
15#include <limits>
16#include <memory>
17
18namespace pairinteraction {
19template <typename Scalar>
21
22template <typename Scalar>
24 if (!system_atom.is_diagonal()) {
25 throw std::invalid_argument("The system must be diagonalized before it can be added.");
26 }
27 systems_atom.push_back(system_atom);
28 return *this;
29}
30
31template <typename Scalar>
33 range_energy = {min, max};
34 return *this;
35}
36
37template <typename Scalar>
39 real_t max) {
40 range_quantum_number_m = {min, max};
41 return *this;
42}
43
44template <typename Scalar>
46 product_of_parities = value;
47 return *this;
48}
49
50template <typename Scalar>
51std::shared_ptr<const BasisPair<Scalar>> BasisPairCreator<Scalar>::create() const {
52 if (systems_atom.size() != 2) {
53 throw std::invalid_argument("Two SystemAtom must be added before creating the BasisPair.");
54 }
55
56 constexpr real_t numerical_precision = 100 * std::numeric_limits<real_t>::epsilon();
57
58 // Sort the states, which are eigenstates, by their energy
59 auto system1 = systems_atom[0].get();
60 auto system2 = systems_atom[1].get();
61 system1.transform(system1.get_sorter({TransformationType::SORT_BY_ENERGY}));
62 system2.transform(system2.get_sorter({TransformationType::SORT_BY_ENERGY}));
63
64 // Construct the canonical basis that contains all KetPair objects with allowed energies and
65 // quantum numbers
66 auto basis1 = system1.get_basis();
67 auto basis2 = system2.get_basis();
68 auto eigenenergies1 = system1.get_eigenenergies();
69 auto eigenenergies2 = system2.get_eigenenergies();
70 real_t *eigenenergies2_begin = eigenenergies2.data();
71 real_t *eigenenergies2_end = eigenenergies2_begin + eigenenergies2.size();
72
73 ketvec_t kets;
74 kets.reserve(eigenenergies1.size() * eigenenergies2.size());
75
76 typename basis_t::map_range_t map_range_of_state_index2;
77 map_range_of_state_index2.reserve(eigenenergies1.size());
78
79 typename basis_t::map_indices_t state_indices_to_ket_index;
80
81 // Loop only over states with an allowed energy
82 size_t ket_index = 0;
83 for (size_t idx1 = 0; idx1 < static_cast<size_t>(eigenenergies1.size()); ++idx1) {
84 // Get the energetically allowed range of the second index
85 size_t min = 0;
86 size_t max = eigenenergies2.size();
87 if (range_energy.is_finite()) {
88 real_t min_val2 = range_energy.min() - eigenenergies1[idx1];
89 real_t max_val2 = range_energy.max() - eigenenergies1[idx1];
90 min =
91 std::distance(eigenenergies2_begin,
92 std::lower_bound(eigenenergies2_begin, eigenenergies2_end, min_val2));
93 max =
94 std::distance(eigenenergies2_begin,
95 std::upper_bound(eigenenergies2_begin, eigenenergies2_end, max_val2));
96 }
97 map_range_of_state_index2.emplace(idx1, typename basis_t::range_t(min, max));
98
99 // Loop over the energetically allowed range of the second index
100 for (size_t idx2 = min; idx2 < max; ++idx2) {
101 // Get energy
102 const real_t energy = eigenenergies1[idx1] + eigenenergies2[idx2];
103 assert(!range_energy.is_finite() ||
104 (energy >= range_energy.min() && energy <= range_energy.max()));
105
106 // Check the parity of the sum of the parities
107 if (product_of_parities != Parity::UNKNOWN) {
108 if (static_cast<int>(basis1->get_parity(idx1)) *
109 static_cast<int>(basis2->get_parity(idx2)) !=
110 static_cast<int>(product_of_parities)) {
111 continue;
112 }
113 }
114
115 // Create a KetPair object
116 auto ket = std::make_shared<ket_t>(
117 typename ket_t::Private(), std::initializer_list<size_t>{idx1, idx2},
118 std::initializer_list<std::shared_ptr<const BasisAtom<Scalar>>>{basis1, basis2},
119 energy);
120
121 // Check the quantum number m
122 if (ket->has_quantum_number_m()) {
123 if (range_quantum_number_m.is_finite() &&
124 (ket->get_quantum_number_m() <
125 range_quantum_number_m.min() - numerical_precision ||
126 ket->get_quantum_number_m() >
127 range_quantum_number_m.max() + numerical_precision)) {
128 continue;
129 }
130 } else if (range_quantum_number_m.is_finite()) {
131 throw std::invalid_argument(
132 "The quantum number m must not be restricted because it is not well-defined.");
133 }
134
135 // Store the KetPair object as a ket
136 kets.emplace_back(std::move(ket));
137
138 // Store the ket index
139 state_indices_to_ket_index.emplace(std::vector<size_t>{idx1, idx2}, ket_index++);
140 }
141 }
142
143 kets.shrink_to_fit();
144
145 return std::make_shared<basis_t>(typename basis_t::Private(), std::move(kets),
146 std::move(map_range_of_state_index2),
147 std::move(state_indices_to_ket_index), basis1, basis2);
148}
149
150// Explicit instantiations
151template class BasisPairCreator<double>;
153} // namespace pairinteraction
BasisPairCreator< Scalar > & restrict_product_of_parities(Parity value)
BasisPairCreator< Scalar > & add(const SystemAtom< Scalar > &system_atom)
std::shared_ptr< const BasisPair< Scalar > > create() const
typename traits::NumTraits< Scalar >::real_t real_t
BasisPairCreator< Scalar > & restrict_quantum_number_m(real_t min, real_t max)
BasisPairCreator< Scalar > & restrict_energy(real_t min, real_t max)
std::vector< std::shared_ptr< const ket_t > > ketvec_t
std::unordered_map< std::vector< size_t >, size_t, utils::hash< std::vector< size_t > > > map_indices_t
Definition: BasisPair.hpp:60
std::unordered_map< size_t, range_t > map_range_t
Definition: BasisPair.hpp:58
bool is_diagonal() const
Definition: System.cpp:321