77 std::vector<RealSchurBlock<Scalar>>
blocks;
87 std::vector<RealSchurBlock<Scalar>>
blocks;
97 Real const real_part = alphar <
Real{} ? -infinity : infinity;
98 Real const imaginary_part = alphai ==
Real{} ?
Real{} : (alphai <
Real{} ? -infinity : infinity);
104template <uni20::LapackReal Scalar>
105std::vector<RealSchurBlock<Scalar>> real_schur_blocks(std::vector<uni20::detail::standard_complex<Scalar>>
const& eigenvalues)
107 std::vector<RealSchurBlock<Scalar>> blocks;
108 for (std::size_t index = 0; index < eigenvalues.size();)
110 if (eigenvalues[index].imag() ==
Scalar{})
112 blocks.push_back(RealSchurBlock<Scalar>{
113 .begin = index, .size = 1, .first_eigenvalue = eigenvalues[index], .second_eigenvalue = {}});
118 if (index + 1 >= eigenvalues.size())
120 throw std::runtime_error(
"LAPACK gees returned an incomplete complex Schur block");
122 if (
uni20::math::abs(eigenvalues[index + 1] - std::conj(eigenvalues[index])) >
126 throw std::runtime_error(
"LAPACK gees returned a non-adjacent complex conjugate Schur block");
129 blocks.push_back(RealSchurBlock<Scalar>{.begin = index,
131 .first_eigenvalue = eigenvalues[index],
132 .second_eigenvalue = eigenvalues[index + 1]});
138template <uni20::LapackReal Scalar>
139std::vector<uni20::complex<Scalar>> eigenvalues_from_schur_blocks(std::vector<RealSchurBlock<Scalar>>
const& blocks)
141 std::vector<uni20::complex<Scalar>> eigenvalues;
142 for (
auto const& block : blocks)
144 eigenvalues.push_back(block.first_eigenvalue);
147 eigenvalues.push_back(block.second_eigenvalue);
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)
158 std::vector<Value> ordered;
159 ordered.reserve(
values.size());
160 for (std::size_t
const index : order)
162 if (index >= original_blocks.size())
164 throw std::invalid_argument(
"real Schur block value order contains an out-of-range block index");
166 RealSchurBlock<Scalar>
const& block = original_blocks[index];
167 for (std::size_t offset = 0; offset < block.size; ++offset)
169 ordered.push_back(values[block.begin + offset]);
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)
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)
184 if (index >= original_blocks.size())
186 throw std::invalid_argument(
"real Schur block order contains an out-of-range block index");
188 auto block = original_blocks[index];
191 ordered.push_back(block);
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)
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)
204 if (index >= block_count)
206 throw std::invalid_argument(
"real Schur leading block order contains an out-of-range block index");
210 throw std::invalid_argument(
"real Schur leading block order contains a duplicate block index");
213 order.push_back(index);
215 for (std::size_t index = 0; index < block_count; ++index)
219 order.push_back(index);
225inline std::vector<std::size_t> complete_leading_index_order(std::size_t count,
226 std::vector<std::size_t>
const& leading_order)
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)
235 throw std::invalid_argument(
"Schur leading order contains an out-of-range index");
239 throw std::invalid_argument(
"Schur leading order contains a duplicate index");
242 order.push_back(index);
244 for (std::size_t index = 0; index <
count; ++index)
248 order.push_back(index);
256template <uni20::LapackReal Scalar>
259 std::size_t
const n = diagonal.size();
260 if (subdiagonal.size() + 1 != n && !(n == 0 && subdiagonal.empty()))
262 throw std::invalid_argument(
"symmetric_tridiagonal_eigenvalues received inconsistent diagonal sizes");
271 no_eigenvectors,
false);
287template <uni20::LapackReal Scalar>
289 std::vector<Scalar> subdiagonal,
bool compute_vectors)
291 std::size_t
const n = diagonal.size();
292 if (subdiagonal.size() + 1 != n && !(n == 0 && subdiagonal.empty()))
294 throw std::invalid_argument(
"symmetric_tridiagonal_eigensystem received inconsistent diagonal sizes");
298 if (!compute_vectors)
333template <uni20::LapackReal Scalar>
338 throw std::invalid_argument(
"real_schur_layout_right requires a square matrix");
341 std::size_t
const n =
static_cast<std::size_t
>(matrix.
rows());
367template <uni20::LapackReal Scalar>
373 throw std::invalid_argument(
"real_schur requires a square matrix");
375 std::size_t
const n =
static_cast<std::size_t
>(matrix.
rows());
397template <uni20::LapackReal Scalar>
400 if (hessenberg.
rows() != hessenberg.
cols())
402 throw std::invalid_argument(
"real_hessenberg_schur requires a square matrix");
405 std::size_t
const n =
static_cast<std::size_t
>(hessenberg.
rows());
434template <uni20::LapackReal Real>
436 bool compute_vectors)
438 if (matrix.rows() != matrix.cols())
440 throw std::invalid_argument(
"complex_schur_layout_right requires a square matrix");
445 std::size_t
const n =
static_cast<std::size_t
>(matrix.rows());
469template <uni20::LapackReal Real>
474 if (matrix.rows() != matrix.cols())
476 throw std::invalid_argument(
"complex_schur requires a square matrix");
478 std::size_t
const n =
static_cast<std::size_t
>(matrix.rows());
503template <uni20::LapackReal Scalar>
505 std::vector<std::size_t>
const& leading_block_order)
509 throw std::invalid_argument(
"reorder_real_schur_layout_right requires a square Schur form");
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())
516 return decomposition;
520 throw std::invalid_argument(
"reorder_real_schur_layout_right received an inconsistent Schur form");
525 throw std::invalid_argument(
"reorder_real_schur_layout_right received inconsistent Schur vectors");
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;
538 for (std::size_t target_position = 0; target_position < leading_block_order.size(); ++target_position)
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())
544 throw std::logic_error(
"real Schur block reorder lost a block index");
546 std::size_t
const current_position =
static_cast<std::size_t
>(current_it - current_order.begin());
547 if (current_position == target_position)
553 current_blocks[target_position].
begin, update_vectors);
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);
563 decomposition.
blocks = detail::ordered_schur_blocks(decomposition.
blocks, current_order);
564 decomposition.
eigenvalues = detail::eigenvalues_from_schur_blocks(decomposition.
blocks);
565 return decomposition;
573template <uni20::LapackReal Scalar>
575 std::vector<std::size_t>
const& leading_block_order)
580 right.eigenvalues = std::move(decomposition.
eigenvalues);
581 right.blocks = std::move(decomposition.
blocks);
589 result.
blocks = std::move(right.blocks);
601template <uni20::LapackReal Real>
602RightComplexSchurDecomposition<Real>
604 std::vector<std::size_t>
const& leading_order)
608 throw std::invalid_argument(
"reorder_complex_schur_layout_right requires a square Schur form");
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())
617 return decomposition;
621 throw std::invalid_argument(
"reorder_complex_schur_layout_right received inconsistent Schur data");
626 throw std::invalid_argument(
"reorder_complex_schur_layout_right received inconsistent Schur vectors");
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});
637 for (std::size_t target_position = 0; target_position < leading_order.size(); ++target_position)
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())
643 throw std::logic_error(
"complex Schur reorder lost an index");
645 std::size_t
const current_position =
static_cast<std::size_t
>(current_it - current_order.begin());
646 if (current_position == target_position)
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);
661 std::vector<Complex> reordered_eigenvalues;
662 reordered_eigenvalues.reserve(n);
663 for (std::size_t
const index : current_order)
665 reordered_eigenvalues.push_back(decomposition.
eigenvalues[index]);
667 decomposition.
eigenvalues = std::move(reordered_eigenvalues);
668 return decomposition;
676template <uni20::LapackReal Real>
678 std::vector<std::size_t>
const& leading_order)
683 right.eigenvalues = std::move(decomposition.
eigenvalues);
694template <uni20::LapackReal Real>
696 bool compute_right_vectors)
700 throw std::invalid_argument(
"real_nonsymmetric_eigensystem requires a square matrix");
703 std::size_t
const n =
static_cast<std::size_t
>(matrix.
rows());
736template <uni20::LapackReal Real>
738 bool compute_right_vectors)
740 if (matrix.rows() != matrix.cols())
742 throw std::invalid_argument(
"complex_nonsymmetric_eigensystem requires a square matrix");
747 std::size_t
const n =
static_cast<std::size_t
>(matrix.rows());
757 compute_right_vectors);
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