19template <
typename Scalar>
22template <
typename Scalar>
25 throw std::invalid_argument(
"The system must be diagonalized before it can be added.");
27 systems_atom.push_back(system_atom);
31template <
typename Scalar>
33 range_energy = {min, max};
37template <
typename Scalar>
40 range_quantum_number_m = {min, max};
44template <
typename Scalar>
46 product_of_parities = value;
50template <
typename Scalar>
52 if (systems_atom.size() != 2) {
53 throw std::invalid_argument(
"Two SystemAtom must be added before creating the BasisPair.");
56 constexpr real_t numerical_precision = 100 * std::numeric_limits<real_t>::epsilon();
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}));
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();
74 kets.reserve(eigenenergies1.size() * eigenenergies2.size());
77 map_range_of_state_index2.reserve(eigenenergies1.size());
83 for (
size_t idx1 = 0; idx1 < static_cast<size_t>(eigenenergies1.size()); ++idx1) {
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];
91 std::distance(eigenenergies2_begin,
92 std::lower_bound(eigenenergies2_begin, eigenenergies2_end, min_val2));
94 std::distance(eigenenergies2_begin,
95 std::upper_bound(eigenenergies2_begin, eigenenergies2_end, max_val2));
97 map_range_of_state_index2.emplace(idx1,
typename basis_t::range_t(min, max));
100 for (
size_t idx2 = min; idx2 < max; ++idx2) {
102 const real_t energy = eigenenergies1[idx1] + eigenenergies2[idx2];
103 assert(!range_energy.is_finite() ||
104 (energy >= range_energy.min() && energy <= range_energy.max()));
108 if (
static_cast<int>(basis1->get_parity(idx1)) *
109 static_cast<int>(basis2->get_parity(idx2)) !=
110 static_cast<int>(product_of_parities)) {
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},
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)) {
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.");
136 kets.emplace_back(std::move(ket));
139 state_indices_to_ket_index.emplace(std::vector<size_t>{idx1, idx2}, ket_index++);
143 kets.shrink_to_fit();
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);
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
std::unordered_map< size_t, range_t > map_range_t