Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
dense_subspace.hpp
Go to the documentation of this file.
1#pragma once
2
4
5#include <algorithm>
6#include <array>
7#include <complex>
8#include <cstddef>
9#include <limits>
10#include <numeric>
11#include <stdexcept>
12#include <utility>
13#include <vector>
14
15namespace uni20::krylov
16{
17
20
21template <typename Scalar> struct TridiagonalEigensystem
22{
23 std::vector<Scalar> eigenvalues;
25};
26
28
29template <uni20::LapackReal Real> struct RealNonsymmetricEigensystem
30{
31 std::vector<uni20::complex<Real>> eigenvalues;
33};
34
37
38template <uni20::LapackReal Scalar> struct RealSchurBlock
39{
40 std::size_t begin = 0;
41 std::size_t size = 0;
44};
45
47
48template <uni20::LapackReal Real> struct ComplexNonsymmetricEigensystem
49{
50 std::vector<uni20::complex<Real>> eigenvalues;
52};
53
62
65template <uni20::LapackReal Real> struct RightComplexSchurDecomposition
66{
69 std::vector<uni20::complex<Real>> eigenvalues;
70};
71
72template <uni20::LapackReal Scalar> struct RealSchurDecomposition
73{
76 std::vector<uni20::complex<Scalar>> eigenvalues;
77 std::vector<RealSchurBlock<Scalar>> blocks;
78};
79
82template <uni20::LapackReal Scalar> struct RightRealSchurDecomposition
83{
86 std::vector<uni20::complex<Scalar>> eigenvalues;
87 std::vector<RealSchurBlock<Scalar>> blocks;
88};
89
90namespace detail
91{
92template <uni20::LapackReal Real> uni20::complex<Real> generalized_eigenvalue(Real alphar, Real alphai, Real beta)
93{
94 if (beta == Real{})
95 {
97 Real const real_part = alphar < Real{} ? -infinity : infinity;
98 Real const imaginary_part = alphai == Real{} ? Real{} : (alphai < Real{} ? -infinity : infinity);
99 return uni20::complex<Real>{real_part, imaginary_part};
100 }
101 return uni20::complex<Real>{alphar, alphai} / beta;
102}
103
104template <uni20::LapackReal Scalar>
105std::vector<RealSchurBlock<Scalar>> real_schur_blocks(std::vector<uni20::detail::standard_complex<Scalar>> const& eigenvalues)
106{
107 std::vector<RealSchurBlock<Scalar>> blocks;
108 for (std::size_t index = 0; index < eigenvalues.size();)
109 {
110 if (eigenvalues[index].imag() == Scalar{})
111 {
112 blocks.push_back(RealSchurBlock<Scalar>{
113 .begin = index, .size = 1, .first_eigenvalue = eigenvalues[index], .second_eigenvalue = {}});
114 ++index;
115 continue;
116 }
117
118 if (index + 1 >= eigenvalues.size())
119 {
120 throw std::runtime_error("LAPACK gees returned an incomplete complex Schur block");
121 }
122 if (uni20::math::abs(eigenvalues[index + 1] - std::conj(eigenvalues[index])) >
124 std::max(Scalar{1}, std::max(uni20::math::abs(eigenvalues[index]), uni20::math::abs(eigenvalues[index + 1]))))
125 {
126 throw std::runtime_error("LAPACK gees returned a non-adjacent complex conjugate Schur block");
127 }
128
129 blocks.push_back(RealSchurBlock<Scalar>{.begin = index,
130 .size = 2,
131 .first_eigenvalue = eigenvalues[index],
132 .second_eigenvalue = eigenvalues[index + 1]});
133 index += 2;
134 }
135 return blocks;
136}
137
138template <uni20::LapackReal Scalar>
139std::vector<uni20::complex<Scalar>> eigenvalues_from_schur_blocks(std::vector<RealSchurBlock<Scalar>> const& blocks)
140{
141 std::vector<uni20::complex<Scalar>> eigenvalues;
142 for (auto const& block : blocks)
143 {
144 eigenvalues.push_back(block.first_eigenvalue);
145 if (block.size == 2)
146 {
147 eigenvalues.push_back(block.second_eigenvalue);
148 }
149 }
150 return eigenvalues;
151}
152
153template <typename Value, uni20::LapackReal Scalar>
154std::vector<Value> ordered_schur_block_values(std::vector<Value> const& values,
155 std::vector<RealSchurBlock<Scalar>> const& original_blocks,
156 std::vector<std::size_t> const& order)
157{
158 std::vector<Value> ordered;
159 ordered.reserve(values.size());
160 for (std::size_t const index : order)
161 {
162 if (index >= original_blocks.size())
163 {
164 throw std::invalid_argument("real Schur block value order contains an out-of-range block index");
165 }
166 RealSchurBlock<Scalar> const& block = original_blocks[index];
167 for (std::size_t offset = 0; offset < block.size; ++offset)
168 {
169 ordered.push_back(values[block.begin + offset]);
170 }
171 }
172 return ordered;
173}
174
175template <uni20::LapackReal Scalar>
176std::vector<RealSchurBlock<Scalar>> ordered_schur_blocks(std::vector<RealSchurBlock<Scalar>> const& original_blocks,
177 std::vector<std::size_t> const& order)
178{
179 std::vector<RealSchurBlock<Scalar>> ordered;
180 ordered.reserve(order.size());
181 std::size_t begin = 0;
182 for (std::size_t const index : order)
183 {
184 if (index >= original_blocks.size())
185 {
186 throw std::invalid_argument("real Schur block order contains an out-of-range block index");
187 }
188 auto block = original_blocks[index];
189 block.begin = begin;
190 begin += block.size;
191 ordered.push_back(block);
192 }
193 return ordered;
194}
195
196inline std::vector<std::size_t> complete_leading_block_order(std::size_t block_count,
197 std::vector<std::size_t> const& leading_block_order)
198{
199 std::vector<bool> seen(block_count, false);
200 std::vector<std::size_t> order;
201 order.reserve(block_count);
202 for (std::size_t const index : leading_block_order)
203 {
204 if (index >= block_count)
205 {
206 throw std::invalid_argument("real Schur leading block order contains an out-of-range block index");
207 }
208 if (seen[index])
209 {
210 throw std::invalid_argument("real Schur leading block order contains a duplicate block index");
211 }
212 seen[index] = true;
213 order.push_back(index);
214 }
215 for (std::size_t index = 0; index < block_count; ++index)
216 {
217 if (!seen[index])
218 {
219 order.push_back(index);
220 }
221 }
222 return order;
223}
224
225inline std::vector<std::size_t> complete_leading_index_order(std::size_t count,
226 std::vector<std::size_t> const& leading_order)
227{
228 std::vector<bool> seen(count, false);
229 std::vector<std::size_t> order;
230 order.reserve(count);
231 for (std::size_t const index : leading_order)
232 {
233 if (index >= count)
234 {
235 throw std::invalid_argument("Schur leading order contains an out-of-range index");
236 }
237 if (seen[index])
238 {
239 throw std::invalid_argument("Schur leading order contains a duplicate index");
240 }
241 seen[index] = true;
242 order.push_back(index);
243 }
244 for (std::size_t index = 0; index < count; ++index)
245 {
246 if (!seen[index])
247 {
248 order.push_back(index);
249 }
250 }
251 return order;
252}
253
254} // namespace detail
255
256template <uni20::LapackReal Scalar>
257std::vector<Scalar> symmetric_tridiagonal_eigenvalues(std::vector<Scalar> diagonal, std::vector<Scalar> subdiagonal)
258{
259 std::size_t const n = diagonal.size();
260 if (subdiagonal.size() + 1 != n && !(n == 0 && subdiagonal.empty()))
261 {
262 throw std::invalid_argument("symmetric_tridiagonal_eigenvalues received inconsistent diagonal sizes");
263 }
264 if (n == 0)
265 {
266 return diagonal;
267 }
268
269 uni20::DenseMatrix<Scalar> no_eigenvectors(0, 0);
270 uni20::linalg::symmetric_tridiagonal_eigen(std::span<Scalar>(diagonal), std::span<Scalar>(subdiagonal),
271 no_eigenvectors, false);
272 return diagonal;
273}
274
287template <uni20::LapackReal Scalar>
289 std::vector<Scalar> subdiagonal, bool compute_vectors)
290{
291 std::size_t const n = diagonal.size();
292 if (subdiagonal.size() + 1 != n && !(n == 0 && subdiagonal.empty()))
293 {
294 throw std::invalid_argument("symmetric_tridiagonal_eigensystem received inconsistent diagonal sizes");
295 }
296
298 if (!compute_vectors)
299 {
300 result.eigenvalues = symmetric_tridiagonal_eigenvalues(std::move(diagonal), std::move(subdiagonal));
302 return result;
303 }
304
305 result.eigenvalues = std::move(diagonal);
306 if (n == 0)
307 {
309 return result;
310 }
311
313 uni20::linalg::symmetric_tridiagonal_eigen(std::span<Scalar>(result.eigenvalues), std::span<Scalar>(subdiagonal), z,
314 true);
315
316 result.eigenvectors = std::move(z);
317 return result;
318}
319
332
333template <uni20::LapackReal Scalar>
335{
336 if (matrix.rows() != matrix.cols())
337 {
338 throw std::invalid_argument("real_schur_layout_right requires a square matrix");
339 }
340
341 std::size_t const n = static_cast<std::size_t>(matrix.rows());
343 result.eigenvalues.resize(n);
344 if (n == 0)
345 {
346 result.schur_form = RightMatrix<Scalar>(0, 0);
347 result.schur_vectors = RightMatrix<Scalar>(0, 0);
348 return result;
349 }
350
351 uni20::DenseMatrix<Scalar> lapack_matrix = copy_right_to_left(matrix);
352 uni20::DenseMatrix<Scalar> schur_vectors_left(compute_vectors ? n : 0, compute_vectors ? n : 0);
353 uni20::linalg::schur(lapack_matrix, std::span<uni20::complex<Scalar>>(result.eigenvalues), schur_vectors_left,
354 compute_vectors);
355
356 result.blocks = detail::real_schur_blocks(result.eigenvalues);
357 result.schur_form = copy_left_to_right(lapack_matrix);
358 result.schur_vectors = compute_vectors ? copy_left_to_right(schur_vectors_left) : RightMatrix<Scalar>(0, 0);
359 return result;
360}
361
367template <uni20::LapackReal Scalar>
369{
371 if (matrix.rows() != matrix.cols())
372 {
373 throw std::invalid_argument("real_schur requires a square matrix");
374 }
375 std::size_t const n = static_cast<std::size_t>(matrix.rows());
376
377 result.schur_form = std::move(matrix);
378 result.schur_vectors = uni20::DenseMatrix<Scalar>(compute_vectors ? n : 0, compute_vectors ? n : 0);
379 result.eigenvalues.resize(n);
380 if (n != 0)
381 {
383 compute_vectors);
384 result.blocks = detail::real_schur_blocks(result.eigenvalues);
385 }
386 return result;
387}
388
397template <uni20::LapackReal Scalar>
399{
400 if (hessenberg.rows() != hessenberg.cols())
401 {
402 throw std::invalid_argument("real_hessenberg_schur requires a square matrix");
403 }
404
405 std::size_t const n = static_cast<std::size_t>(hessenberg.rows());
407 result.eigenvalues.resize(n);
408 if (n == 0)
409 {
412 return result;
413 }
414
415 uni20::DenseMatrix<Scalar> schur_vectors(compute_vectors ? n : 0, compute_vectors ? n : 0);
416 uni20::linalg::hessenberg_schur(hessenberg, std::span<uni20::complex<Scalar>>(result.eigenvalues), schur_vectors,
417 compute_vectors);
418 result.blocks = detail::real_schur_blocks(result.eigenvalues);
419 result.schur_form = std::move(hessenberg);
420 result.schur_vectors = compute_vectors ? std::move(schur_vectors) : uni20::DenseMatrix<Scalar>(0, 0);
421 return result;
422}
423
434template <uni20::LapackReal Real>
436 bool compute_vectors)
437{
438 if (matrix.rows() != matrix.cols())
439 {
440 throw std::invalid_argument("complex_schur_layout_right requires a square matrix");
441 }
442
444
445 std::size_t const n = static_cast<std::size_t>(matrix.rows());
447 result.eigenvalues.resize(n);
448 if (n == 0)
449 {
450 result.schur_form = RightMatrix<Complex>(0, 0);
451 result.schur_vectors = RightMatrix<Complex>(0, 0);
452 return result;
453 }
454
455 uni20::DenseMatrix<Complex> lapack_matrix = copy_right_to_left(matrix);
456 uni20::DenseMatrix<Complex> schur_vectors_left(compute_vectors ? n : 0, compute_vectors ? n : 0);
457 uni20::linalg::schur(lapack_matrix, std::span<Complex>(result.eigenvalues), schur_vectors_left, compute_vectors);
458
459 result.schur_form = copy_left_to_right(lapack_matrix);
460 result.schur_vectors = compute_vectors ? copy_left_to_right(schur_vectors_left) : RightMatrix<Complex>(0, 0);
461 return result;
462}
463
469template <uni20::LapackReal Real>
470ComplexSchurDecomposition<Real> complex_schur(uni20::DenseMatrix<uni20::detail::standard_complex<Real>> matrix, bool compute_vectors)
471{
474 if (matrix.rows() != matrix.cols())
475 {
476 throw std::invalid_argument("complex_schur requires a square matrix");
477 }
478 std::size_t const n = static_cast<std::size_t>(matrix.rows());
479
480 result.schur_form = std::move(matrix);
481 result.schur_vectors = uni20::DenseMatrix<Complex>(compute_vectors ? n : 0, compute_vectors ? n : 0);
482 result.eigenvalues.resize(n);
483 if (n != 0)
484 {
485 uni20::linalg::schur(result.schur_form, std::span<Complex>(result.eigenvalues), result.schur_vectors,
486 compute_vectors);
487 }
488 return result;
489}
490
503template <uni20::LapackReal Scalar>
505 std::vector<std::size_t> const& leading_block_order)
506{
507 if (decomposition.schur_form.rows() != decomposition.schur_form.cols())
508 {
509 throw std::invalid_argument("reorder_real_schur_layout_right requires a square Schur form");
510 }
511
512 auto const order = decomposition.schur_form.rows();
513 std::size_t const n = static_cast<std::size_t>(order);
514 if (n == 0 || leading_block_order.empty())
515 {
516 return decomposition;
517 }
518 if (decomposition.schur_form.cols() != order)
519 {
520 throw std::invalid_argument("reorder_real_schur_layout_right received an inconsistent Schur form");
521 }
522 bool const update_vectors = decomposition.schur_vectors.rows() != 0 || decomposition.schur_vectors.cols() != 0;
523 if (update_vectors && (decomposition.schur_vectors.rows() != order || decomposition.schur_vectors.cols() != order))
524 {
525 throw std::invalid_argument("reorder_real_schur_layout_right received inconsistent Schur vectors");
526 }
527
528 std::vector<std::size_t> target_order =
529 detail::complete_leading_block_order(decomposition.blocks.size(), leading_block_order);
530 std::vector<std::size_t> current_order(decomposition.blocks.size());
531 std::iota(current_order.begin(), current_order.end(), std::size_t{0});
532 std::vector<RealSchurBlock<Scalar>> current_blocks = decomposition.blocks;
533
534 uni20::DenseMatrix<Scalar> schur_form = copy_right_to_left(decomposition.schur_form);
535 uni20::DenseMatrix<Scalar> schur_vectors =
536 update_vectors ? copy_right_to_left(decomposition.schur_vectors) : uni20::DenseMatrix<Scalar>(0, 0);
537
538 for (std::size_t target_position = 0; target_position < leading_block_order.size(); ++target_position)
539 {
540 std::size_t const desired_original_index = target_order[target_position];
541 auto const current_it = std::ranges::find(current_order, desired_original_index);
542 if (current_it == current_order.end())
543 {
544 throw std::logic_error("real Schur block reorder lost a block index");
545 }
546 std::size_t const current_position = static_cast<std::size_t>(current_it - current_order.begin());
547 if (current_position == target_position)
548 {
549 continue;
550 }
551
552 uni20::linalg::reorder_schur(schur_form, schur_vectors, current_blocks[current_position].begin,
553 current_blocks[target_position].begin, update_vectors);
554
555 std::size_t const moved_index = current_order[current_position];
556 current_order.erase(current_order.begin() + static_cast<std::ptrdiff_t>(current_position));
557 current_order.insert(current_order.begin() + static_cast<std::ptrdiff_t>(target_position), moved_index);
558 current_blocks = detail::ordered_schur_blocks(decomposition.blocks, current_order);
559 }
560
561 decomposition.schur_form = copy_left_to_right(schur_form);
562 decomposition.schur_vectors = update_vectors ? copy_left_to_right(schur_vectors) : RightMatrix<Scalar>(0, 0);
563 decomposition.blocks = detail::ordered_schur_blocks(decomposition.blocks, current_order);
564 decomposition.eigenvalues = detail::eigenvalues_from_schur_blocks(decomposition.blocks);
565 return decomposition;
566}
567
573template <uni20::LapackReal Scalar>
575 std::vector<std::size_t> const& leading_block_order)
576{
578 right.schur_form = copy_left_to_right(decomposition.schur_form);
579 right.schur_vectors = copy_left_to_right(decomposition.schur_vectors);
580 right.eigenvalues = std::move(decomposition.eigenvalues);
581 right.blocks = std::move(decomposition.blocks);
582
583 right = reorder_real_schur_layout_right(std::move(right), leading_block_order);
584
586 result.schur_form = copy_right_to_left(right.schur_form);
587 result.schur_vectors = copy_right_to_left(right.schur_vectors);
588 result.eigenvalues = std::move(right.eigenvalues);
589 result.blocks = std::move(right.blocks);
590 return result;
591}
592
600
601template <uni20::LapackReal Real>
602RightComplexSchurDecomposition<Real>
604 std::vector<std::size_t> const& leading_order)
605{
606 if (decomposition.schur_form.rows() != decomposition.schur_form.cols())
607 {
608 throw std::invalid_argument("reorder_complex_schur_layout_right requires a square Schur form");
609 }
610
612
613 auto const order = decomposition.schur_form.rows();
614 std::size_t const n = static_cast<std::size_t>(order);
615 if (n == 0 || leading_order.empty())
616 {
617 return decomposition;
618 }
619 if (decomposition.schur_form.cols() != order || decomposition.eigenvalues.size() != n)
620 {
621 throw std::invalid_argument("reorder_complex_schur_layout_right received inconsistent Schur data");
622 }
623 bool const update_vectors = decomposition.schur_vectors.rows() != 0 || decomposition.schur_vectors.cols() != 0;
624 if (update_vectors && (decomposition.schur_vectors.rows() != order || decomposition.schur_vectors.cols() != order))
625 {
626 throw std::invalid_argument("reorder_complex_schur_layout_right received inconsistent Schur vectors");
627 }
628
629 std::vector<std::size_t> target_order = detail::complete_leading_index_order(n, leading_order);
630 std::vector<std::size_t> current_order(n);
631 std::iota(current_order.begin(), current_order.end(), std::size_t{0});
632
634 uni20::DenseMatrix<Complex> schur_vectors =
635 update_vectors ? copy_right_to_left(decomposition.schur_vectors) : uni20::DenseMatrix<Complex>(0, 0);
636
637 for (std::size_t target_position = 0; target_position < leading_order.size(); ++target_position)
638 {
639 std::size_t const desired_original_index = target_order[target_position];
640 auto const current_it = std::ranges::find(current_order, desired_original_index);
641 if (current_it == current_order.end())
642 {
643 throw std::logic_error("complex Schur reorder lost an index");
644 }
645 std::size_t const current_position = static_cast<std::size_t>(current_it - current_order.begin());
646 if (current_position == target_position)
647 {
648 continue;
649 }
650
651 uni20::linalg::reorder_schur(schur_form, schur_vectors, current_position, target_position, update_vectors);
652
653 std::size_t const moved_index = current_order[current_position];
654 current_order.erase(current_order.begin() + static_cast<std::ptrdiff_t>(current_position));
655 current_order.insert(current_order.begin() + static_cast<std::ptrdiff_t>(target_position), moved_index);
656 }
657
658 decomposition.schur_form = copy_left_to_right(schur_form);
659 decomposition.schur_vectors = update_vectors ? copy_left_to_right(schur_vectors) : RightMatrix<Complex>(0, 0);
660
661 std::vector<Complex> reordered_eigenvalues;
662 reordered_eigenvalues.reserve(n);
663 for (std::size_t const index : current_order)
664 {
665 reordered_eigenvalues.push_back(decomposition.eigenvalues[index]);
666 }
667 decomposition.eigenvalues = std::move(reordered_eigenvalues);
668 return decomposition;
669}
670
676template <uni20::LapackReal Real>
678 std::vector<std::size_t> const& leading_order)
679{
681 right.schur_form = copy_left_to_right(decomposition.schur_form);
682 right.schur_vectors = copy_left_to_right(decomposition.schur_vectors);
683 right.eigenvalues = std::move(decomposition.eigenvalues);
684
685 right = reorder_complex_schur_layout_right(std::move(right), leading_order);
686
688 result.schur_form = copy_right_to_left(right.schur_form);
689 result.schur_vectors = copy_right_to_left(right.schur_vectors);
690 result.eigenvalues = std::move(right.eigenvalues);
691 return result;
692}
693
694template <uni20::LapackReal Real>
696 bool compute_right_vectors)
697{
698 if (matrix.rows() != matrix.cols())
699 {
700 throw std::invalid_argument("real_nonsymmetric_eigensystem requires a square matrix");
701 }
702
703 std::size_t const n = static_cast<std::size_t>(matrix.rows());
705 result.eigenvalues.resize(n);
706 result.right_eigenvectors =
707 uni20::DenseMatrix<uni20::complex<Real>>(compute_right_vectors ? n : 0, compute_right_vectors ? n : 0);
708 if (n == 0)
709 {
710 return result;
711 }
712
714 result.right_eigenvectors, compute_right_vectors);
715
716 return result;
717}
718
735
736template <uni20::LapackReal Real>
738 bool compute_right_vectors)
739{
740 if (matrix.rows() != matrix.cols())
741 {
742 throw std::invalid_argument("complex_nonsymmetric_eigensystem requires a square matrix");
743 }
744
746
747 std::size_t const n = static_cast<std::size_t>(matrix.rows());
749 result.eigenvalues.resize(n);
750 result.right_eigenvectors = uni20::DenseMatrix<Complex>(compute_right_vectors ? n : 0, compute_right_vectors ? n : 0);
751 if (n == 0)
752 {
753 return result;
754 }
755
756 uni20::linalg::nonsymmetric_eigen(matrix, std::span<Complex>(result.eigenvalues), result.right_eigenvectors,
757 compute_right_vectors);
758 return result;
759}
760
761} // namespace uni20::krylov
General-purpose owning tensor with immediate or deferred mdspan metadata.
Definition basic_tensor.hpp:109
auto rows() const noexcept
Return the matrix row count for rank-two tensors.
Definition basic_tensor.hpp:768
auto cols() const noexcept
Return the matrix column count for rank-two tensors.
Definition basic_tensor.hpp:776
Concept for complex scalar types.
Definition scalar_concepts.hpp:75
Concept for real scalar types (float, double, etc.).
Definition scalar_concepts.hpp:69
Concept for numeric scalar types (integer, real, or complex).
Definition scalar_concepts.hpp:81
typename detail::complex_type< Real >::type complex
Project-level complex scalar spelling.
Definition types.hpp:66
Definition block_tensor_vector.hpp:23
ComplexSchurDecomposition< Real > complex_schur(uni20::DenseMatrix< uni20::detail::standard_complex< Real > > matrix, bool compute_vectors)
Compute the complex Schur decomposition of a column-major DenseMatrix.
Definition dense_subspace.hpp:470
RealSchurDecomposition< Scalar > real_hessenberg_schur(uni20::DenseMatrix< Scalar > hessenberg, bool compute_vectors)
Compute the real Schur decomposition of an upper Hessenberg matrix through LAPACK hseqr.
Definition dense_subspace.hpp:398
RealNonsymmetricEigensystem< Real > real_nonsymmetric_eigensystem(uni20::DenseMatrix< Real > matrix, bool compute_right_vectors)
Definition dense_subspace.hpp:695
uni20::DenseMatrix< Scalar > copy_right_to_left(RightMatrix< Scalar > const &matrix)
Copy a row-major matrix into a column-major DenseMatrix.
Definition dense_linalg.hpp:63
RightComplexSchurDecomposition< Real > reorder_complex_schur_layout_right(RightComplexSchurDecomposition< Real > decomposition, std::vector< std::size_t > const &leading_order)
Move selected real Schur blocks to the leading invariant subspace and estimate conditioning.
Definition dense_subspace.hpp:603
std::vector< Scalar > symmetric_tridiagonal_eigenvalues(std::vector< Scalar > diagonal, std::vector< Scalar > subdiagonal)
Definition dense_subspace.hpp:257
RightRealSchurDecomposition< Scalar > real_schur_layout_right(RightMatrix< Scalar > matrix, bool compute_vectors)
Solve a real symmetric tridiagonal eigensystem through LAPACK stevd.
Definition dense_subspace.hpp:334
ComplexSchurDecomposition< Real > reorder_complex_schur(ComplexSchurDecomposition< Real > decomposition, std::vector< std::size_t > const &leading_order)
Reorder a column-major complex Schur decomposition by moving selected entries to the front.
Definition dense_subspace.hpp:677
RightRealSchurDecomposition< Scalar > reorder_real_schur_layout_right(RightRealSchurDecomposition< Scalar > decomposition, std::vector< std::size_t > const &leading_block_order)
Reorder a layout-right real Schur decomposition by moving selected blocks to the front.
Definition dense_subspace.hpp:504
RealSchurDecomposition< Scalar > reorder_real_schur(RealSchurDecomposition< Scalar > decomposition, std::vector< std::size_t > const &leading_block_order)
Reorder a column-major real Schur decomposition by moving selected blocks to the front.
Definition dense_subspace.hpp:574
TridiagonalEigensystem< Scalar > symmetric_tridiagonal_eigensystem(std::vector< Scalar > diagonal, std::vector< Scalar > subdiagonal, bool compute_vectors)
Solve a real symmetric tridiagonal eigensystem through LAPACK sterf or steqr.
Definition dense_subspace.hpp:288
RightMatrix< Scalar > copy_left_to_right(uni20::DenseMatrix< Scalar > const &matrix)
Copy a column-major DenseMatrix into a row-major matrix.
Definition dense_linalg.hpp:46
ComplexNonsymmetricEigensystem< Real > complex_nonsymmetric_eigensystem(uni20::DenseMatrix< uni20::detail::standard_complex< Real > > matrix, bool compute_right_vectors)
Solve a dense real nonsymmetric eigensystem through LAPACK geevx.
Definition dense_subspace.hpp:737
RightComplexSchurDecomposition< Real > complex_schur_layout_right(RightMatrix< uni20::detail::standard_complex< Real > > matrix, bool compute_vectors)
Compute the complex Schur decomposition of a layout-right matrix through LAPACK gees.
Definition dense_subspace.hpp:435
RealSchurDecomposition< Scalar > real_schur(uni20::DenseMatrix< Scalar > matrix, bool compute_vectors)
Compute the real Schur decomposition of a column-major DenseMatrix.
Definition dense_subspace.hpp:368
auto values(Tensor &tensor)
Definition packed_matrix.hpp:29
void hessenberg_schur(BackendSelector &&selector, MatrixTensor &&matrix_work, std::span< EigenScalar > eigenvalues, SchurVectorTensor &&schur_vectors, bool compute_vectors)
Replace a real upper-Hessenberg matrix by its real Schur form.
Definition ops/schur.hpp:69
void reorder_schur(BackendSelector &&selector, SchurFormTensor &&schur_form, SchurVectorTensor &&schur_vectors, std::size_t from, std::size_t to, bool update_vectors)
Reorder a Schur form in place, optionally updating its Schur vectors.
Definition ops/schur.hpp:109
void schur(BackendSelector &&selector, MatrixTensor &&matrix_work, std::span< EigenScalar > eigenvalues, SchurVectorTensor &&schur_vectors, bool compute_vectors)
Replace a square matrix by its Schur form and optionally return Schur vectors.
Definition ops/schur.hpp:37
void nonsymmetric_eigen(BackendSelector &&selector, MatrixTensor &&matrix_work, std::span< EigenScalar > eigenvalues, RightEigenvectorTensor &&right_eigenvectors, bool compute_right_vectors, std::optional< Precision > requested={})
Compute eigenvalues and optional right eigenvectors, destroying the input matrix.
Definition ops/nonsymmetric_eigen.hpp:40
void symmetric_tridiagonal_eigen(BackendSelector &&selector, std::span< Scalar > diagonal, std::span< Scalar > subdiagonal, EigenvectorTensor &&eigenvectors, bool compute_vectors)
Overwrite a real symmetric tridiagonal matrix with its eigensystem.
Definition ops/tridiagonal_eigen.hpp:34
constexpr detail::scalar_math::abs_fn abs
Absolute value or complex magnitude, preserving the selected scalar implementation.
Definition math.hpp:459
mpreal beta(mpreal const &a, mpreal const &x)
Real Beta function; positive rational/integer pairs retain exact values.
Definition mpreal_special.hpp:164
Generalized real Schur decomposition of a dense real matrix pencil.
Definition dense_subspace.hpp:49
std::vector< uni20::complex< Real > > eigenvalues
Definition dense_subspace.hpp:50
uni20::DenseMatrix< uni20::complex< Real > > right_eigenvectors
Definition dense_subspace.hpp:51
Complex Schur decomposition stored in column-major DenseMatrix values.
Definition dense_subspace.hpp:57
uni20::DenseMatrix< uni20::complex< Real > > schur_vectors
Definition dense_subspace.hpp:59
std::vector< uni20::complex< Real > > eigenvalues
Definition dense_subspace.hpp:60
uni20::DenseMatrix< uni20::complex< Real > > schur_form
Definition dense_subspace.hpp:58
Eigenvalues and optional eigenvectors of a dense real symmetric matrix.
Definition dense_subspace.hpp:30
std::vector< uni20::complex< Real > > eigenvalues
Definition dense_subspace.hpp:31
uni20::DenseMatrix< uni20::complex< Real > > right_eigenvectors
Definition dense_subspace.hpp:32
Expert eigensystem diagnostics for a dense real nonsymmetric matrix.
Definition dense_subspace.hpp:39
uni20::complex< Scalar > first_eigenvalue
Definition dense_subspace.hpp:42
std::size_t begin
Definition dense_subspace.hpp:40
uni20::complex< Scalar > second_eigenvalue
Definition dense_subspace.hpp:43
std::size_t size
Definition dense_subspace.hpp:41
Definition dense_subspace.hpp:73
uni20::DenseMatrix< Scalar > schur_form
Definition dense_subspace.hpp:74
std::vector< uni20::complex< Scalar > > eigenvalues
Definition dense_subspace.hpp:76
uni20::DenseMatrix< Scalar > schur_vectors
Definition dense_subspace.hpp:75
std::vector< RealSchurBlock< Scalar > > blocks
Definition dense_subspace.hpp:77
Complex Schur decomposition stored in prototype layout-right matrices.
Definition dense_subspace.hpp:66
RightMatrix< uni20::complex< Real > > schur_vectors
Definition dense_subspace.hpp:68
std::vector< uni20::complex< Real > > eigenvalues
Definition dense_subspace.hpp:69
RightMatrix< uni20::complex< Real > > schur_form
Definition dense_subspace.hpp:67
Real Schur decomposition stored in prototype layout-right matrices.
Definition dense_subspace.hpp:83
RightMatrix< Scalar > schur_form
Definition dense_subspace.hpp:84
std::vector< RealSchurBlock< Scalar > > blocks
Definition dense_subspace.hpp:87
std::vector< uni20::complex< Scalar > > eigenvalues
Definition dense_subspace.hpp:86
RightMatrix< Scalar > schur_vectors
Definition dense_subspace.hpp:85
Eigenvalues and optional eigenvectors of a symmetric tridiagonal matrix.
Definition dense_subspace.hpp:22
std::vector< Scalar > eigenvalues
Definition dense_subspace.hpp:23
uni20::DenseMatrix< Scalar > eigenvectors
Definition dense_subspace.hpp:24
Project-level numeric limits customization point.
Definition numeric_limits.hpp:16