Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
backends/lapack/nonsymmetric_eigen.hpp
Go to the documentation of this file.
1#pragma once
2
9#include "common.hpp"
10
20
21#include <algorithm>
22#include <concepts>
23#include <span>
24#include <type_traits>
25#include <vector>
26
27namespace uni20::linalg
28{
29namespace lapack_detail
30{
31
33template <uni20::MutableRankedStridedMdspanLike<2> MatrixMdspan, class EigenScalar,
34 uni20::MutableRankedMdspanLike<2> RightEigenvectorMdspan>
36{
37 using matrix_scalar = std::remove_cv_t<typename MatrixMdspan::element_type>;
38 using real_type = uni20::make_real_t<matrix_scalar>;
39 using expected_eigen_scalar = uni20::complex<real_type>;
40 using vector_scalar = std::remove_cv_t<typename RightEigenvectorMdspan::element_type>;
41
42 if constexpr (uni20::LapackScalar<matrix_scalar> && std::same_as<EigenScalar, expected_eigen_scalar> &&
43 std::same_as<vector_scalar, expected_eigen_scalar> && uni20::DefaultAccessorMdspanLike<MatrixMdspan>)
44 {
45 return kernel_types_maybe;
46 }
47 else
48 {
49 return kernel_types_no;
50 }
51}
52
54template <uni20::MutableRankedStridedMdspanLike<2> MatrixMdspan, class EigenScalar,
55 uni20::MutableRankedMdspanLike<2> RightEigenvectorMdspan>
56KernelAttempt try_nonsymmetric_eigen(nonsymmetric_eigen_op const& op, MatrixMdspan& matrix_work,
57 std::span<EigenScalar> eigenvalues, RightEigenvectorMdspan& right_eigenvectors)
58{
59 using matrix_type = std::remove_cvref_t<MatrixMdspan>;
60 using matrix_scalar = std::remove_cv_t<typename matrix_type::element_type>;
61 using real_type = uni20::make_real_t<matrix_scalar>;
62 using complex_type = uni20::complex<real_type>;
63 using index_type = typename std::remove_cvref_t<RightEigenvectorMdspan>::index_type;
64
65 CHECK_EQUAL(matrix_work.extent(0), matrix_work.extent(1));
66 std::size_t const n = static_cast<std::size_t>(matrix_work.extent(0));
67 CHECK_EQUAL(eigenvalues.size(), n);
68 CHECK(!op.compute_right_vectors || (static_cast<std::size_t>(right_eigenvectors.extent(0)) == n &&
69 static_cast<std::size_t>(right_eigenvectors.extent(1)) == n));
70
71 blas_int const order = uni20::blas::try_blas_int(n);
73 {
75 }
76
77 auto matrix = uni20::linalg::blas::try_lapack_writable_matrix(matrix_work);
78 if (!matrix)
79 {
81 }
82 CHECK_EQUAL(matrix->rows, order);
83 CHECK_EQUAL(matrix->cols, order);
84 if (order == 0)
85 {
87 }
88
89 char const jobvr = op.compute_right_vectors ? 'V' : 'N';
90 blas_int const ldvr = op.compute_right_vectors ? order : 1;
91 std::size_t vector_storage_size = 1;
93 {
94 auto const square_size = lapack_detail::try_size_product(n, n);
95 if (!square_size)
96 {
98 }
99 vector_storage_size = *square_size;
100 }
101
103 {
104 std::vector<matrix_scalar> wr(n, matrix_scalar{});
105 std::vector<matrix_scalar> wi(n, matrix_scalar{});
106 std::vector<matrix_scalar> vl(1, matrix_scalar{});
107 std::vector<matrix_scalar> vr(vector_storage_size, matrix_scalar{});
108
109 matrix_scalar work_query{};
110 uni20::lapack::geev('N', jobvr, order, matrix->data, matrix->leading_dimension, wr.data(), wi.data(), vl.data(), 1,
111 vr.data(), ldvr, &work_query, -1);
112 blas_int const lwork = lapack_detail::workspace_size(work_query);
113 std::vector<matrix_scalar> work(static_cast<std::size_t>(lwork), matrix_scalar{});
114 uni20::lapack::geev('N', jobvr, order, matrix->data, matrix->leading_dimension, wr.data(), wi.data(), vl.data(), 1,
115 vr.data(), ldvr, work.data(), lwork);
116
117 for (std::size_t col = 0; col < n; ++col)
118 {
119 eigenvalues[col] = complex_type{wr[col], wi[col]};
120 }
121
122 if (!op.compute_right_vectors)
123 {
125 }
126
127 for (std::size_t col = 0; col < n; ++col)
128 {
129 if (wi[col] == real_type{})
130 {
131 for (std::size_t row = 0; row < n; ++row)
132 {
133 right_eigenvectors[static_cast<index_type>(row), static_cast<index_type>(col)] =
134 complex_type{vr[row + col * n], real_type{}};
135 }
136 }
137 else if (wi[col] > real_type{})
138 {
139 CHECK(col + 1 < n, "LAPACK geev returned an incomplete conjugate eigenvector pair");
140 for (std::size_t row = 0; row < n; ++row)
141 {
142 complex_type const value{vr[row + col * n], vr[row + (col + 1) * n]};
143 right_eigenvectors[static_cast<index_type>(row), static_cast<index_type>(col)] = value;
144 right_eigenvectors[static_cast<index_type>(row), static_cast<index_type>(col + 1)] = uni20::conj(value);
145 }
146 ++col;
147 }
148 else
149 {
150 PANIC("LAPACK geev returned an unpaired negative-imaginary eigenvalue", col, wi[col]);
151 }
152 }
153 }
154 else
155 {
156 auto const twice_order = lapack_detail::try_size_product(2, n);
157 if (!twice_order)
158 {
160 }
161 std::vector<complex_type> values(n, complex_type{});
162 std::vector<complex_type> vl(1, complex_type{});
163 std::vector<complex_type> vr(vector_storage_size, complex_type{});
164 std::vector<real_type> rwork(std::max<std::size_t>(1, *twice_order), real_type{});
165
166 complex_type work_query{};
167 uni20::lapack::geev('N', jobvr, order, matrix->data, matrix->leading_dimension, values.data(), vl.data(), 1,
168 vr.data(), ldvr, &work_query, -1, rwork.data());
169 blas_int const lwork = lapack_detail::workspace_size(work_query);
170 std::vector<complex_type> work(static_cast<std::size_t>(lwork), complex_type{});
171 uni20::lapack::geev('N', jobvr, order, matrix->data, matrix->leading_dimension, values.data(), vl.data(), 1,
172 vr.data(), ldvr, work.data(), lwork, rwork.data());
173
174 std::ranges::copy(values, eigenvalues.begin());
176 {
177 for (std::size_t col = 0; col < n; ++col)
178 {
179 for (std::size_t row = 0; row < n; ++row)
180 {
181 right_eigenvectors[static_cast<index_type>(row), static_cast<index_type>(col)] = vr[row + col * n];
182 }
183 }
184 }
185 }
186
188}
189} // namespace lapack_detail
190
192template <uni20::MutableRankedStridedMdspecLike<2> MatrixMdspan, class EigenScalar,
193 uni20::MutableRankedMdspecLike<2> RightEigenvectorMdspan>
196consteval auto kernel_accepts_types(LapackBackend const&, nonsymmetric_eigen_op const&, MatrixMdspan&,
197 std::span<EigenScalar>&, RightEigenvectorMdspan&)
198{
201 constexpr auto acceptance = lapack_detail::nonsymmetric_eigen_acceptance<matrix_span, EigenScalar, vector_span>();
202 if constexpr (acceptance == KernelTypeAcceptance::no)
203 return kernel_types_no;
204 else
205 return kernel_types_maybe;
206}
207
209template <uni20::MutableRankedStridedMdspecLike<2> MatrixMdspan, class EigenScalar,
210 uni20::MutableRankedMdspecLike<2> RightEigenvectorMdspan>
213KernelAttempt try_kernel(LapackBackend, nonsymmetric_eigen_op const& operation, MatrixMdspan& matrix_work,
214 std::span<EigenScalar> eigenvalues, RightEigenvectorMdspan& right_eigenvectors)
215{
217 [&](auto& matrix_span, auto& vector_span) {
218 return lapack_detail::try_nonsymmetric_eigen(operation, matrix_span, eigenvalues, vector_span);
219 },
220 matrix_work, right_eigenvectors);
221}
222
223} // namespace uni20::linalg
224
225#if UNI20_HAS_MPLAPACK_MPREAL || UNI20_HAS_MPLAPACK_FLOAT80
227#endif
RAII leases that resolve device descriptors and tensor views to mdspans.
Safe conversion helpers for the configured signed BLAS integer ABI.
An mdspan-like type whose accessor is stdex::default_accessor.
Definition mdspan/concepts.hpp:501
Mutable mdspec metadata supporting host write acquisition.
Definition access.hpp:694
Real scalar types supported by the configured raw-pointer LAPACK wrappers.
Definition scalar_concepts.hpp:115
Concept for scalar types with a configured dense LAPACK backend.
Definition scalar_concepts.hpp:147
locale_t value
Definition data_table.cpp:49
Logical host adapters for value-converting dense decompositions.
Operation-tag backend dispatch over tensor-view operands.
uni20::complex< T > conj(detail::standard_complex< T > x)
Returns the complex conjugate for complex-valued scalars.
Definition math.hpp:70
typename detail::complex_type< Real >::type complex
Project-level complex scalar spelling.
Definition types.hpp:66
typename make_real< T >::type make_real_t
Alias for the underlying real-valued type of T.
Definition scalar_traits.hpp:168
Checked LAPACK wrappers over provider-specific unchecked overloads.
Shared helpers for LAPACK operation-tag backend adapters.
Lower mdspan matrix views to provider-ready BLAS operands.
constexpr blas_int try_blas_int(Value value) noexcept
Convert a non-negative integer to blas_int, or return the negative invalid sentinel.
Definition blas_int.hpp:28
constexpr bool is_valid_blas_int(blas_int value) noexcept
Return whether a non-throwing BLAS integer conversion succeeded.
Definition blas_int.hpp:25
void geev(char jobvl, char jobvr, blas_int n, Scalar *a, blas_int lda, Scalar *wr, Scalar *wi, Scalar *vl, blas_int ldvl, Scalar *vr, blas_int ldvr, Scalar *work, blas_int lwork)
Compute eigenvalues and optionally eigenvectors of a real nonsymmetric matrix.
Definition lapack.hpp:790
auto try_lapack_writable_matrix(Mdspan const &span) -> std::optional< BlasWritableMatrix< std::remove_cv_t< typename Mdspan::element_type >, typename Mdspan::data_handle_type > >
Try to lower an mdspan to the strict column-major shape expected by LAPACK.
Definition mdspan_matrix.hpp:285
blas_int workspace_size(Scalar const &query)
Convert a provider workspace query result to the configured LAPACK integer type.
Definition linalg/backends/lapack/common.hpp:43
KernelAttempt with_host_write_mdspans(Function &&function, Mdspecs &... mdspecs)
Invoke a LAPACK attempt with host-writable mdspans under simultaneous leases.
Definition linalg/backends/lapack/common.hpp:29
KernelAttempt try_nonsymmetric_eigen(nonsymmetric_eigen_op const &op, MatrixMdspan &matrix_work, std::span< EigenScalar > eigenvalues, RightEigenvectorMdspan &right_eigenvectors)
Compute eigenvalues and optional right eigenvectors through LAPACK geev.
Definition backends/lapack/nonsymmetric_eigen.hpp:56
consteval auto nonsymmetric_eigen_acceptance()
Report compile-time eligibility for resolved LAPACK nonsymmetric eigenanalysis.
Definition backends/lapack/nonsymmetric_eigen.hpp:35
constexpr std::optional< std::size_t > try_size_product(std::size_t left, std::size_t right) noexcept
Multiply allocation extents without wrapping std::size_t.
Definition linalg/backends/lapack/common.hpp:69
Definition linalg/async.hpp:28
KernelAttempt try_kernel(BlasBackend, gemm_op const &, C &c, S const &alpha, A &a, B &b, S const &beta)
Pack logical host values and execute GEMM through value-preserving provider conversion.
Definition linalg/backends/blas/binary80.hpp:30
KernelAttempt
Result of one runtime kernel backend attempt.
Definition kernel_attempt.hpp:27
constexpr KernelAcceptance< KernelTypeAcceptance::no > kernel_types_no
Report that a backend rejects the argument types.
Definition dispatch_error.hpp:34
consteval auto kernel_accepts_types(BlasBackend, gemm_op const &, C &, S const &, A &, B &, S const &)
Accept host-readable native binary80 operands, including transforming accessors.
Definition linalg/backends/blas/binary80.hpp:19
constexpr KernelAcceptance< KernelTypeAcceptance::maybe > kernel_types_maybe
Report that runtime operand values determine backend acceptance.
Definition dispatch_error.hpp:37
std::remove_cvref_t< decltype(std::declval< detail::host_write_mdspan_lease_t< Mdspec > & >().mdspan())> host_write_mdspan_t
Writable mdspan type resolved by host descriptor acquisition.
Definition access.hpp:684
std::ptrdiff_t index_type
Definition types.hpp:19
Backend-independent operation values used with normalized kernel operands.
Backend family for configured dense LAPACK kernels, including MPLAPACK scalar adapters.
Definition backend_selector.hpp:20
Dense nonsymmetric eigensystem operation tag.
Definition operation_tags.hpp:308
bool compute_right_vectors
Definition operation_tags.hpp:310
Concepts for tensor-level objects that resolve mdspan kernel operands.
#define CHECK(cond,...)
Definition trace.hpp:299
#define PANIC(...)
Definition trace.hpp:382
#define CHECK_EQUAL(a, b,...)
Definition trace.hpp:309