22template <
typename Scalar>
25template <
typename Derived>
28 for (
const auto &label : labels) {
30 throw std::invalid_argument(
"One of the labels is not a valid sorting label.");
35template <
typename Derived>
37 const std::set<TransformationType> &unique_labels)
const {
39 std::set<TransformationType> unique_labels_present;
40 for (
const auto &label : get_transformation().transformation_type) {
41 if (!
utils::is_sorting(label) || unique_labels_present.size() >= unique_labels.size()) {
44 unique_labels_present.insert(label);
46 if (unique_labels != unique_labels_present) {
47 throw std::invalid_argument(
"The states are not sorted by the requested labels.");
53 throw std::invalid_argument(
"States do not store the energy and thus no energy blocks can "
54 "be obtained. Use an energy operator instead.");
58template <
typename Derived>
60 : kets(std::move(kets)), coefficients{{
static_cast<Eigen::Index
>(this->kets.size()),
61 static_cast<Eigen::Index
>(this->kets.size())},
63 if (this->kets.empty()) {
64 throw std::invalid_argument(
"The basis must contain at least one element.");
66 state_index_to_quantum_number_f.reserve(this->kets.size());
67 state_index_to_quantum_number_m.reserve(this->kets.size());
68 state_index_to_parity.reserve(this->kets.size());
69 ket_to_ket_index.reserve(this->kets.size());
71 for (
const auto &ket : this->kets) {
72 state_index_to_quantum_number_f.push_back(ket->get_quantum_number_f());
73 state_index_to_quantum_number_m.push_back(ket->get_quantum_number_m());
74 state_index_to_parity.push_back(ket->get_parity());
75 ket_to_ket_index[ket] = index++;
76 if (ket->get_quantum_number_f() == std::numeric_limits<real_t>::max()) {
77 _has_quantum_number_f =
false;
79 if (ket->get_quantum_number_m() == std::numeric_limits<real_t>::max()) {
80 _has_quantum_number_m =
false;
82 if (ket->get_parity() == Parity::UNKNOWN) {
86 state_index_to_ket_index.resize(this->kets.size());
87 std::iota(state_index_to_ket_index.begin(), state_index_to_ket_index.end(), 0);
88 ket_index_to_state_index.resize(this->kets.size());
89 std::iota(ket_index_to_state_index.begin(), ket_index_to_state_index.end(), 0);
90 coefficients.matrix.setIdentity();
93template <
typename Derived>
95 return _has_quantum_number_f;
98template <
typename Derived>
100 return _has_quantum_number_m;
103template <
typename Derived>
108template <
typename Derived>
110 return static_cast<const Derived &
>(*this);
113template <
typename Derived>
118template <
typename Derived>
119const Eigen::SparseMatrix<typename Basis<Derived>::scalar_t, Eigen::RowMajor> &
121 return coefficients.matrix;
124template <
typename Derived>
125Eigen::SparseMatrix<typename Basis<Derived>::scalar_t, Eigen::RowMajor> &
127 return coefficients.matrix;
130template <
typename Derived>
132 const Eigen::SparseMatrix<scalar_t, Eigen::RowMajor> &values) {
133 if (values.rows() != coefficients.matrix.rows()) {
134 throw std::invalid_argument(
"Incompatible number of rows.");
136 if (values.cols() != coefficients.matrix.cols()) {
137 throw std::invalid_argument(
"Incompatible number of columns.");
140 coefficients.matrix = values;
142 std::fill(ket_index_to_state_index.begin(), ket_index_to_state_index.end(),
143 std::numeric_limits<int>::max());
144 std::fill(state_index_to_ket_index.begin(), state_index_to_ket_index.end(),
145 std::numeric_limits<int>::max());
146 std::fill(state_index_to_quantum_number_f.begin(), state_index_to_quantum_number_f.end(),
147 std::numeric_limits<real_t>::max());
148 std::fill(state_index_to_quantum_number_m.begin(), state_index_to_quantum_number_m.end(),
149 std::numeric_limits<real_t>::max());
150 std::fill(state_index_to_parity.begin(), state_index_to_parity.end(), Parity::UNKNOWN);
151 _has_quantum_number_f =
false;
152 _has_quantum_number_m =
false;
156template <
typename Derived>
158 if (ket_to_ket_index.count(ket) == 0) {
161 return ket_to_ket_index.at(ket);
164template <
typename Derived>
167 int ket_index = get_ket_index_from_ket(ket);
169 throw std::invalid_argument(
"The ket does not belong to the basis.");
173 return coefficients.matrix.row(ket_index);
176template <
typename Derived>
177Eigen::SparseMatrix<typename Basis<Derived>::scalar_t, Eigen::RowMajor>
179 return other->coefficients.
matrix.adjoint() * coefficients.matrix;
182template <
typename Derived>
185 return get_amplitudes(ket).cwiseAbs2();
188template <
typename Derived>
189Eigen::SparseMatrix<typename Basis<Derived>::real_t, Eigen::RowMajor>
191 return get_amplitudes(other).cwiseAbs2();
194template <
typename Derived>
196 real_t quantum_number_f = state_index_to_quantum_number_f.at(state_index);
197 if (quantum_number_f == std::numeric_limits<real_t>::max()) {
198 throw std::invalid_argument(
"The state does not have a well-defined quantum number f.");
200 return quantum_number_f;
203template <
typename Derived>
205 real_t quantum_number_m = state_index_to_quantum_number_m.at(state_index);
206 if (quantum_number_m == std::numeric_limits<real_t>::max()) {
207 throw std::invalid_argument(
"The state does not have a well-defined quantum number m.");
209 return quantum_number_m;
212template <
typename Derived>
214 Parity parity = state_index_to_parity.at(state_index);
215 if (parity == Parity::UNKNOWN) {
216 throw std::invalid_argument(
"The state does not have a well-defined parity.");
221template <
typename Derived>
222std::shared_ptr<const typename Basis<Derived>::ket_t>
224 size_t ket_index = state_index_to_ket_index.at(state_index);
225 if (ket_index == std::numeric_limits<int>::max()) {
226 throw std::invalid_argument(
"The state does not belong to a ket in a well-defined way.");
228 return kets[ket_index];
231template <
typename Derived>
232std::shared_ptr<const typename Basis<Derived>::ket_t>
234 throw std::runtime_error(
"Not implemented yet.");
237template <
typename Derived>
240 auto restricted = std::make_shared<Derived>(derived());
243 restricted->coefficients.matrix = restricted->coefficients.matrix.col(state_index);
245 std::fill(restricted->ket_index_to_state_index.begin(),
246 restricted->ket_index_to_state_index.end(), std::numeric_limits<int>::max());
247 restricted->ket_index_to_state_index[state_index_to_ket_index[state_index]] = 0;
249 restricted->state_index_to_quantum_number_f = {state_index_to_quantum_number_f[state_index]};
250 restricted->state_index_to_quantum_number_m = {state_index_to_quantum_number_m[state_index]};
251 restricted->state_index_to_parity = {state_index_to_parity[state_index]};
252 restricted->state_index_to_ket_index = {state_index_to_ket_index[state_index]};
254 restricted->_has_quantum_number_f =
255 restricted->state_index_to_quantum_number_f[0] != std::numeric_limits<real_t>::max();
256 restricted->_has_quantum_number_m =
257 restricted->state_index_to_quantum_number_m[0] != std::numeric_limits<real_t>::max();
258 restricted->_has_parity = restricted->state_index_to_parity[0] != Parity::UNKNOWN;
263template <
typename Derived>
264std::shared_ptr<const typename Basis<Derived>::ket_t>
266 return kets[ket_index];
269template <
typename Derived>
271 size_t state_index = ket_index_to_state_index.at(ket_index);
272 if (state_index == std::numeric_limits<int>::max()) {
273 throw std::runtime_error(
"The ket does not belong to a state in a well-defined way.");
275 return get_state(state_index);
278template <
typename Derived>
279std::shared_ptr<const Derived>
281 int ket_index = get_ket_index_from_ket(ket);
283 throw std::invalid_argument(
"The ket does not belong to the basis.");
285 return get_corresponding_state(ket_index);
288template <
typename Derived>
290 int state_index = ket_index_to_state_index.at(ket_index);
291 if (state_index == std::numeric_limits<int>::max()) {
292 throw std::runtime_error(
"The ket does not belong to a state in a well-defined way.");
297template <
typename Derived>
299 int ket_index = get_ket_index_from_ket(ket);
301 throw std::invalid_argument(
"The ket does not belong to the basis.");
303 return get_corresponding_state_index(ket_index);
306template <
typename Derived>
308 int ket_index = state_index_to_ket_index.at(state_index);
309 if (ket_index == std::numeric_limits<int>::max()) {
310 throw std::runtime_error(
"The state does not belong to a ket in a well-defined way.");
315template <
typename Derived>
317 throw std::runtime_error(
"Not implemented yet.");
320template <
typename Derived>
321std::shared_ptr<const Derived>
324 auto created = std::make_shared<Derived>(derived());
327 created->coefficients.matrix =
328 Eigen::SparseMatrix<scalar_t, Eigen::RowMajor>(coefficients.matrix.rows(), 1);
329 created->coefficients.matrix.coeffRef(ket_index, 0) = 1;
330 created->coefficients.matrix.makeCompressed();
332 std::fill(created->ket_index_to_state_index.begin(), created->ket_index_to_state_index.end(),
333 std::numeric_limits<int>::max());
334 created->ket_index_to_state_index[ket_index] = 0;
336 created->state_index_to_quantum_number_f = {kets[ket_index]->get_quantum_number_f()};
337 created->state_index_to_quantum_number_m = {kets[ket_index]->get_quantum_number_m()};
338 created->state_index_to_parity = {kets[ket_index]->get_parity()};
339 created->state_index_to_ket_index = {ket_index};
341 created->_has_quantum_number_f =
342 created->state_index_to_quantum_number_f[0] != std::numeric_limits<real_t>::max();
343 created->_has_quantum_number_m =
344 created->state_index_to_quantum_number_m[0] != std::numeric_limits<real_t>::max();
345 created->_has_parity = created->state_index_to_parity[0] != Parity::UNKNOWN;
350template <
typename Derived>
351std::shared_ptr<const Derived>
353 int ket_index = get_ket_index_from_ket(ket);
355 throw std::invalid_argument(
"The ket does not belong to the basis.");
357 return get_canonical_state_from_ket(ket_index);
360template <
typename Derived>
365template <
typename Derived>
370template <
typename Derived>
373template <
typename Derived>
375 return other.it != it;
378template <
typename Derived>
383template <
typename Derived>
389template <
typename Derived>
391 return coefficients.
matrix.cols();
394template <
typename Derived>
396 return coefficients.
matrix.rows();
399template <
typename Derived>
405template <
typename Derived>
409 static_cast<Eigen::Index
>(coefficients.
matrix.rows())},
412 std::vector<Eigen::Triplet<scalar_t>> entries;
414 for (
size_t idx_initial = 0; idx_initial <
kets.size(); ++idx_initial) {
415 real_t f =
kets[idx_initial]->get_quantum_number_f();
416 real_t m_initial =
kets[idx_initial]->get_quantum_number_m();
418 assert(2 * f == std::floor(2 * f) && f >= 0);
419 assert(2 * m_initial == std::floor(2 * m_initial) && m_initial >= -f && m_initial <= f);
421 for (
real_t m_final = -f; m_final <= f;
423 auto val = wigner::wigner_uppercase_d_matrix<scalar_t>(f, m_initial, m_final, alpha,
426 kets[idx_initial]->get_ket_for_different_quantum_number_m(m_final));
427 entries.emplace_back(idx_final, idx_initial, val);
431 transformation.matrix.setFromTriplets(entries.begin(), entries.end());
432 transformation.matrix.makeCompressed();
434 return transformation;
437template <
typename Derived>
444 throw std::invalid_argument(
"States do not store the energy and thus can not be sorted by "
445 "the energy. Use an energy operator instead.");
450 transformation.
matrix.resize(coefficients.
matrix.cols());
451 transformation.
matrix.setIdentity();
458 throw std::invalid_argument(
"The states could not be sorted by all the requested labels.");
461 return transformation;
464template <
typename Derived>
465std::vector<IndicesOfBlock>
469 std::set<TransformationType> unique_labels(labels.begin(), labels.end());
476 return blocks_creator.create();
479template <
typename Derived>
481 Sorting &transformation)
const {
482 constexpr real_t numerical_precision = 100 * std::numeric_limits<real_t>::epsilon();
484 int *perm_begin = transformation.
matrix.indices().data();
485 int *perm_end = perm_begin + coefficients.
matrix.cols();
486 const int *perm_back = perm_end - 1;
489 std::stable_sort(perm_begin, perm_end, [&](
int a,
int b) {
490 for (
const auto &label : labels) {
493 if (state_index_to_parity[a] != state_index_to_parity[b]) {
494 return state_index_to_parity[a] < state_index_to_parity[b];
498 if (std::abs(state_index_to_quantum_number_m[a] -
499 state_index_to_quantum_number_m[b]) > numerical_precision) {
500 return state_index_to_quantum_number_m[a] < state_index_to_quantum_number_m[b];
504 if (std::abs(state_index_to_quantum_number_f[a] -
505 state_index_to_quantum_number_f[b]) > numerical_precision) {
506 return state_index_to_quantum_number_f[a] < state_index_to_quantum_number_f[b];
510 if (state_index_to_ket_index[a] != state_index_to_ket_index[b]) {
511 return state_index_to_ket_index[a] < state_index_to_ket_index[b];
522 for (
const auto &label : labels) {
526 throw std::invalid_argument(
527 "States cannot be labeled and thus not sorted by the parity.");
532 if (state_index_to_quantum_number_m[*perm_back] == std::numeric_limits<real_t>::max()) {
533 throw std::invalid_argument(
534 "States cannot be labeled and thus not sorted by the quantum number m.");
540 if (state_index_to_quantum_number_f[*perm_back] == std::numeric_limits<real_t>::max()) {
541 throw std::invalid_argument(
542 "States cannot be labeled and thus not sorted by the quantum number f.");
548 if (state_index_to_ket_index[*perm_back] == std::numeric_limits<int>::max()) {
549 throw std::invalid_argument(
550 "States cannot be labeled and thus not sorted by kets.");
560template <
typename Derived>
562 const std::set<TransformationType> &unique_labels,
564 constexpr real_t numerical_precision = 100 * std::numeric_limits<real_t>::epsilon();
566 auto last_quantum_number_f = state_index_to_quantum_number_f[0];
567 auto last_quantum_number_m = state_index_to_quantum_number_m[0];
568 auto last_parity = state_index_to_parity[0];
569 auto last_ket = state_index_to_ket_index[0];
571 for (
int i = 0; i < coefficients.
matrix.cols(); ++i) {
572 for (
auto label : unique_labels) {
574 if (std::abs(state_index_to_quantum_number_f[i] - last_quantum_number_f) >
575 numerical_precision) {
576 blocks_creator.
add(i);
580 if (std::abs(state_index_to_quantum_number_m[i] - last_quantum_number_m) >
581 numerical_precision) {
582 blocks_creator.
add(i);
586 if (state_index_to_parity[i] != last_parity) {
587 blocks_creator.
add(i);
591 if (state_index_to_ket_index[i] != last_ket) {
592 blocks_creator.
add(i);
597 last_quantum_number_f = state_index_to_quantum_number_f[i];
598 last_quantum_number_m = state_index_to_quantum_number_m[i];
599 last_parity = state_index_to_parity[i];
600 last_ket = state_index_to_ket_index[i];
604template <
typename Derived>
607 auto transformed = std::make_shared<Derived>(derived());
609 if (coefficients.
matrix.cols() == 0) {
617 transformed->state_index_to_quantum_number_f.resize(transformation.
matrix.size());
618 transformed->state_index_to_quantum_number_m.resize(transformation.
matrix.size());
622 for (
int i = 0; i < transformation.
matrix.size(); ++i) {
624 state_index_to_quantum_number_f[transformation.
matrix.indices()[i]];
626 state_index_to_quantum_number_m[transformation.
matrix.indices()[i]];
628 state_index_to_parity[transformation.
matrix.indices()[i]];
630 state_index_to_ket_index[transformation.
matrix.indices()[i]];
632 [state_index_to_ket_index[transformation.
matrix.indices()[i]]] = i;
638template <
typename Derived>
639std::shared_ptr<const Derived>
643 real_t numerical_precision = 0.001;
646 bool is_rotation =
false;
654 throw std::invalid_argument(
"A rotation can not be combined with other transformations.");
661 throw std::runtime_error(
662 "If the object was transformed by a different transformation "
663 "than sorting, it can not be rotated.");
669 auto transformed = std::make_shared<Derived>(derived());
671 if (coefficients.
matrix.cols() == 0) {
681 Eigen::SparseMatrix<real_t> probs = transformation.
matrix.cwiseAbs2().transpose();
684 auto map = Eigen::Map<const Eigen::VectorX<real_t>>(state_index_to_quantum_number_f.data(),
685 state_index_to_quantum_number_f.size());
689 transformed->state_index_to_quantum_number_f.resize(probs.rows());
691 for (
size_t i = 0; i <
transformed->state_index_to_quantum_number_f.size(); ++i) {
692 if (diff[i] < numerical_precision) {
693 transformed->state_index_to_quantum_number_f[i] = std::round(val[i] * 2) / 2;
696 std::numeric_limits<real_t>::max();
703 auto map = Eigen::Map<const Eigen::VectorX<real_t>>(state_index_to_quantum_number_m.data(),
704 state_index_to_quantum_number_m.size());
708 transformed->state_index_to_quantum_number_m.resize(probs.rows());
710 for (
size_t i = 0; i <
transformed->state_index_to_quantum_number_m.size(); ++i) {
711 if (diff[i] < numerical_precision) {
712 transformed->state_index_to_quantum_number_m[i] = std::round(val[i] * 2) / 2;
715 std::numeric_limits<real_t>::max();
722 using utype = std::underlying_type<Parity>::type;
724 for (
size_t i = 0; i < state_index_to_parity.size(); ++i) {
725 map[i] =
static_cast<utype
>(state_index_to_parity[i]);
730 transformed->state_index_to_parity.resize(probs.rows());
732 for (
size_t i = 0; i <
transformed->state_index_to_parity.size(); ++i) {
733 if (diff[i] < numerical_precision) {
734 transformed->state_index_to_parity[i] =
static_cast<Parity>(std::lround(val[i]));
746 std::vector<real_t> max_in_row(
transformed->coefficients.matrix.rows(), 0);
747 std::vector<real_t> max_in_col(
transformed->coefficients.matrix.cols(), 0);
748 for (
int row = 0; row <
transformed->coefficients.matrix.outerSize(); ++row) {
749 for (
typename Eigen::SparseMatrix<scalar_t, Eigen::RowMajor>::InnerIterator it(
752 real_t val = std::pow(std::abs(it.value()), 2);
753 max_in_row[row] = std::max(max_in_row[row], val);
754 max_in_col[it.col()] = std::max(max_in_col[it.col()], val);
759 std::vector<real_t> costs;
760 std::vector<std::pair<int, int>> mappings;
761 costs.reserve(
transformed->coefficients.matrix.nonZeros());
762 mappings.reserve(
transformed->coefficients.matrix.nonZeros());
763 for (
int row = 0; row <
transformed->coefficients.matrix.outerSize(); ++row) {
764 for (
typename Eigen::SparseMatrix<scalar_t, Eigen::RowMajor>::InnerIterator it(
767 real_t val = std::pow(std::abs(it.value()), 2);
768 real_t cost = max_in_row[row] + max_in_col[it.col()] - 2 * val;
769 costs.push_back(cost);
770 mappings.push_back({row, it.col()});
775 std::vector<size_t> order(costs.size());
776 std::iota(order.begin(), order.end(), 0);
777 std::sort(order.begin(), order.end(),
778 [&](
size_t a,
size_t b) { return costs[a] < costs[b]; });
781 std::fill(
transformed->ket_index_to_state_index.begin(),
782 transformed->ket_index_to_state_index.end(), std::numeric_limits<int>::max());
785 std::vector<bool> row_used(
transformed->coefficients.matrix.rows(),
false);
786 std::vector<bool> col_used(
transformed->coefficients.matrix.cols(),
false);
788 for (
size_t idx : order) {
789 int row = mappings[idx].first;
790 int col = mappings[idx].second;
791 if (!row_used[row] && !col_used[col]) {
792 row_used[row] =
true;
793 col_used[col] =
true;
798 if (num_used ==
transformed->coefficients.matrix.cols()) {
802 assert(num_used ==
transformed->coefficients.matrix.cols());
808template <
typename Derived>
810 return typename ket_t::hash()(*k);
813template <
typename Derived>
814bool Basis<Derived>::equal_to::operator()(
const std::shared_ptr<const ket_t> &lhs,
815 const std::shared_ptr<const ket_t> &rhs)
const {
820template class Basis<BasisAtom<double>>;
821template class Basis<BasisAtom<std::complex<double>>>;
822template class Basis<BasisPair<double>>;
823template class Basis<BasisPair<std::complex<double>>>;
std::shared_ptr< const ket_t > operator*() const
bool operator!=(const Iterator &other) const
const Transformation< scalar_t > & get_transformation() const override
void get_indices_of_blocks_without_checks(const std::set< TransformationType > &unique_labels, IndicesOfBlocksCreator &blocks) const
void get_sorter_without_checks(const std::vector< TransformationType > &labels, Sorting &transformation) const
int get_ket_index_from_ket(std::shared_ptr< const ket_t > ket) const
typename traits::CrtpTraits< Derived >::ketvec_t ketvec_t
size_t get_number_of_states() const
Transformation< scalar_t > get_rotator(real_t alpha, real_t beta, real_t gamma) const override
void perform_sorter_checks(const std::vector< TransformationType > &labels) const
void perform_blocks_checks(const std::set< TransformationType > &unique_labels) const
std::vector< IndicesOfBlock > get_indices_of_blocks(const std::vector< TransformationType > &labels) const override
typename traits::CrtpTraits< Derived >::real_t real_t
std::shared_ptr< const Derived > transformed(const Transformation< scalar_t > &transformation) const
Sorting get_sorter(const std::vector< TransformationType > &labels) const override
size_t get_number_of_kets() const
void add(size_t boundary)
Matrix< Type, Dynamic, 1 > VectorX
bool is_sorting(TransformationType label)
@ SORT_BY_QUANTUM_NUMBER_M
@ SORT_BY_QUANTUM_NUMBER_F
Eigen::PermutationMatrix< Eigen::Dynamic, Eigen::Dynamic > matrix
std::vector< TransformationType > transformation_type