Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
backends/lapack/schur.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::MutableRankedStridedMdspanLike<2> SchurVectorMdspan>
35consteval auto schur_acceptance()
36{
37 using matrix_scalar = std::remove_cv_t<typename MatrixMdspan::element_type>;
38 using vector_scalar = std::remove_cv_t<typename SchurVectorMdspan::element_type>;
39 using expected_eigen_scalar = uni20::complex<uni20::make_real_t<matrix_scalar>>;
40
41 if constexpr (uni20::LapackScalar<matrix_scalar> && std::same_as<vector_scalar, matrix_scalar> &&
42 std::same_as<EigenScalar, 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::MutableRankedStridedMdspanLike<2> SchurVectorMdspan>
56KernelAttempt try_schur(schur_op const& op, MatrixMdspan& matrix_work, std::span<EigenScalar> eigenvalues,
57 SchurVectorMdspan& schur_vectors)
58{
59 using matrix_type = std::remove_cvref_t<MatrixMdspan>;
60 using scalar_type = std::remove_cv_t<typename matrix_type::element_type>;
61 using real_type = uni20::make_real_t<scalar_type>;
62 using complex_type = uni20::complex<real_type>;
63
64 CHECK_EQUAL(matrix_work.extent(0), matrix_work.extent(1));
65 std::size_t const n = static_cast<std::size_t>(matrix_work.extent(0));
66 CHECK_EQUAL(eigenvalues.size(), n);
67 CHECK(!op.compute_vectors || (static_cast<std::size_t>(schur_vectors.extent(0)) == n &&
68 static_cast<std::size_t>(schur_vectors.extent(1)) == n));
69
70 blas_int const order = uni20::blas::try_blas_int(n);
72 {
74 }
75
76 auto matrix = uni20::linalg::blas::try_lapack_writable_matrix(matrix_work);
77 if (!matrix)
78 {
80 }
81
82 decltype(uni20::linalg::blas::try_lapack_writable_matrix(schur_vectors)) vectors;
83 if (op.compute_vectors)
84 {
86 if (!vectors)
87 {
89 }
90 }
91
92 CHECK_EQUAL(matrix->rows, order);
93 CHECK_EQUAL(matrix->cols, order);
94 if (order == 0)
95 {
97 }
98
99 scalar_type dummy{};
100 scalar_type* vector_data = op.compute_vectors ? vectors->data : &dummy;
101 blas_int const ldvs = op.compute_vectors ? vectors->leading_dimension : 1;
102 char const jobvs = op.compute_vectors ? 'V' : 'N';
103 blas_int selected_dimension = 0;
104 std::vector<blas_int> bwork(std::max<std::size_t>(1, n), 0);
105
106 if constexpr (uni20::LapackReal<scalar_type>)
107 {
108 std::vector<scalar_type> wr(n, scalar_type{});
109 std::vector<scalar_type> wi(n, scalar_type{});
110 scalar_type work_query{};
111 uni20::lapack::gees(jobvs, 'N', order, matrix->data, matrix->leading_dimension, selected_dimension, wr.data(),
112 wi.data(), vector_data, ldvs, &work_query, -1, bwork.data());
113 blas_int const lwork = lapack_detail::workspace_size(work_query);
114 std::vector<scalar_type> work(static_cast<std::size_t>(lwork), scalar_type{});
115 uni20::lapack::gees(jobvs, 'N', order, matrix->data, matrix->leading_dimension, selected_dimension, wr.data(),
116 wi.data(), vector_data, ldvs, work.data(), lwork, bwork.data());
117 for (std::size_t index = 0; index < n; ++index)
118 {
119 eigenvalues[index] = complex_type{wr[index], wi[index]};
120 }
121 }
122 else
123 {
124 std::vector<complex_type> values(n, complex_type{});
125 std::vector<real_type> rwork(std::max<std::size_t>(1, n), real_type{});
126 complex_type work_query{};
127 uni20::lapack::gees(jobvs, 'N', order, matrix->data, matrix->leading_dimension, selected_dimension, values.data(),
128 vector_data, ldvs, &work_query, -1, rwork.data(), bwork.data());
129 blas_int const lwork = lapack_detail::workspace_size(work_query);
130 std::vector<complex_type> work(static_cast<std::size_t>(lwork), complex_type{});
131 uni20::lapack::gees(jobvs, 'N', order, matrix->data, matrix->leading_dimension, selected_dimension, values.data(),
132 vector_data, ldvs, work.data(), lwork, rwork.data(), bwork.data());
133 std::ranges::copy(values, eigenvalues.begin());
134 }
135
137}
138
140template <uni20::MutableRankedStridedMdspanLike<2> MatrixMdspan, class EigenScalar,
141 uni20::MutableRankedStridedMdspanLike<2> SchurVectorMdspan>
143{
144 using matrix_scalar = std::remove_cv_t<typename MatrixMdspan::element_type>;
145 using vector_scalar = std::remove_cv_t<typename SchurVectorMdspan::element_type>;
146 if constexpr (uni20::LapackReal<matrix_scalar> && std::same_as<vector_scalar, matrix_scalar> &&
147 std::same_as<EigenScalar, uni20::complex<matrix_scalar>> &&
149 {
150 return kernel_types_maybe;
151 }
152 else
153 {
154 return kernel_types_no;
155 }
156}
157
159template <uni20::MutableRankedStridedMdspanLike<2> MatrixMdspan, class EigenScalar,
160 uni20::MutableRankedStridedMdspanLike<2> SchurVectorMdspan>
161KernelAttempt try_hessenberg_schur(hessenberg_schur_op const& op, MatrixMdspan& hessenberg,
162 std::span<EigenScalar> eigenvalues, SchurVectorMdspan& schur_vectors)
163{
164 using scalar_type = std::remove_cv_t<typename std::remove_cvref_t<MatrixMdspan>::element_type>;
165
166 CHECK_EQUAL(hessenberg.extent(0), hessenberg.extent(1));
167 std::size_t const n = static_cast<std::size_t>(hessenberg.extent(0));
168 CHECK_EQUAL(eigenvalues.size(), n);
169 CHECK(!op.compute_vectors || (static_cast<std::size_t>(schur_vectors.extent(0)) == n &&
170 static_cast<std::size_t>(schur_vectors.extent(1)) == n));
171
172 blas_int const order = uni20::blas::try_blas_int(n);
174 {
176 }
177
178 auto matrix = uni20::linalg::blas::try_lapack_writable_matrix(hessenberg);
179 if (!matrix)
180 {
182 }
183
184 decltype(uni20::linalg::blas::try_lapack_writable_matrix(schur_vectors)) vectors;
185 if (op.compute_vectors)
186 {
188 if (!vectors)
189 {
191 }
192 }
193 if (order == 0)
194 {
196 }
197
198 scalar_type dummy{};
199 scalar_type* vector_data = op.compute_vectors ? vectors->data : &dummy;
200 blas_int const ldz = op.compute_vectors ? vectors->leading_dimension : 1;
201 std::vector<scalar_type> wr(n, scalar_type{});
202 std::vector<scalar_type> wi(n, scalar_type{});
203 scalar_type work_query{};
204 uni20::lapack::hseqr('S', op.compute_vectors ? 'I' : 'N', order, 1, order, matrix->data, matrix->leading_dimension,
205 wr.data(), wi.data(), vector_data, ldz, &work_query, -1);
206 blas_int const lwork = lapack_detail::workspace_size(work_query);
207 std::vector<scalar_type> work(static_cast<std::size_t>(lwork), scalar_type{});
208 uni20::lapack::hseqr('S', op.compute_vectors ? 'I' : 'N', order, 1, order, matrix->data, matrix->leading_dimension,
209 wr.data(), wi.data(), vector_data, ldz, work.data(), lwork);
210 for (std::size_t index = 0; index < n; ++index)
211 {
212 eigenvalues[index] = uni20::complex<scalar_type>{wr[index], wi[index]};
213 }
215}
216
218template <uni20::MutableRankedStridedMdspanLike<2> SchurFormMdspan,
219 uni20::MutableRankedStridedMdspanLike<2> SchurVectorMdspan>
221{
222 using form_scalar = std::remove_cv_t<typename SchurFormMdspan::element_type>;
223 using vector_scalar = std::remove_cv_t<typename SchurVectorMdspan::element_type>;
224 if constexpr (uni20::LapackScalar<form_scalar> && std::same_as<form_scalar, vector_scalar> &&
227 {
228 return kernel_types_maybe;
229 }
230 else
231 {
232 return kernel_types_no;
233 }
234}
235
237template <uni20::MutableRankedStridedMdspanLike<2> SchurFormMdspan,
238 uni20::MutableRankedStridedMdspanLike<2> SchurVectorMdspan>
239KernelAttempt try_schur_reorder(schur_reorder_op const& op, SchurFormMdspan& schur_form,
240 SchurVectorMdspan& schur_vectors)
241{
242 using form_type = std::remove_cvref_t<SchurFormMdspan>;
243 using scalar_type = std::remove_cv_t<typename form_type::element_type>;
244
245 CHECK_EQUAL(schur_form.extent(0), schur_form.extent(1));
246 std::size_t const n = static_cast<std::size_t>(schur_form.extent(0));
247 CHECK(op.from < n);
248 CHECK(op.to < n);
249 CHECK(!op.update_vectors || (static_cast<std::size_t>(schur_vectors.extent(0)) == n &&
250 static_cast<std::size_t>(schur_vectors.extent(1)) == n));
251
252 blas_int const order = uni20::blas::try_blas_int(n);
254 {
256 }
257
259 if (!form)
260 {
262 }
263
264 decltype(uni20::linalg::blas::try_lapack_writable_matrix(schur_vectors)) vectors;
265 if (op.update_vectors)
266 {
268 if (!vectors)
269 {
271 }
272 }
273
274 if (op.from == op.to)
275 {
277 }
278
279 scalar_type dummy{};
280 scalar_type* vector_data = op.update_vectors ? vectors->data : &dummy;
281 blas_int const ldq = op.update_vectors ? vectors->leading_dimension : 1;
282 char const compq = op.update_vectors ? 'V' : 'N';
283 blas_int first = static_cast<blas_int>(op.from + 1);
284 blas_int last = static_cast<blas_int>(op.to + 1);
285
286 if constexpr (uni20::LapackReal<scalar_type>)
287 {
288 std::vector<scalar_type> work(n, scalar_type{});
289 uni20::lapack::trexc(compq, order, form->data, form->leading_dimension, vector_data, ldq, first, last, work.data());
290 }
291 else
292 {
293 uni20::lapack::trexc(compq, order, form->data, form->leading_dimension, vector_data, ldq, first, last);
294 }
296}
297} // namespace lapack_detail
298
300template <class Operation, uni20::MutableRankedStridedMdspecLike<2> MatrixMdspan, class EigenScalar,
301 uni20::MutableRankedStridedMdspecLike<2> SchurVectorMdspan>
302 requires(std::same_as<Operation, schur_op> || std::same_as<Operation, hessenberg_schur_op>) &&
304consteval auto kernel_accepts_types(LapackBackend const&, Operation const&, MatrixMdspan&, std::span<EigenScalar>&,
305 SchurVectorMdspan&)
306{
309 constexpr auto acceptance = [] {
310 if constexpr (std::same_as<Operation, schur_op>)
311 return lapack_detail::schur_acceptance<matrix_span, EigenScalar, vector_span>();
312 else
313 return lapack_detail::hessenberg_schur_acceptance<matrix_span, EigenScalar, vector_span>();
314 }();
315 if constexpr (acceptance == KernelTypeAcceptance::no)
316 return kernel_types_no;
317 else
318 return kernel_types_maybe;
319}
320
322template <class Operation, uni20::MutableRankedStridedMdspecLike<2> MatrixMdspan, class EigenScalar,
323 uni20::MutableRankedStridedMdspecLike<2> SchurVectorMdspan>
324 requires(std::same_as<Operation, schur_op> || std::same_as<Operation, hessenberg_schur_op>) &&
326KernelAttempt try_kernel(LapackBackend, Operation const& operation, MatrixMdspan& matrix_work,
327 std::span<EigenScalar> eigenvalues, SchurVectorMdspan& schur_vectors)
328{
330 [&](auto& matrix_span, auto& vector_span) {
331 if constexpr (std::same_as<Operation, schur_op>)
332 return lapack_detail::try_schur(operation, matrix_span, eigenvalues, vector_span);
333 else
334 return lapack_detail::try_hessenberg_schur(operation, matrix_span, eigenvalues, vector_span);
335 },
336 matrix_work, schur_vectors);
337}
338
340template <uni20::MutableRankedStridedMdspecLike<2> SchurFormMdspan,
341 uni20::MutableRankedStridedMdspecLike<2> SchurVectorMdspan>
343consteval auto kernel_accepts_types(LapackBackend const&, schur_reorder_op const&, SchurFormMdspan&, SchurVectorMdspan&)
344{
347 constexpr auto acceptance = lapack_detail::schur_reorder_acceptance<form_span, vector_span>();
348 if constexpr (acceptance == KernelTypeAcceptance::no)
349 return kernel_types_no;
350 else
351 return kernel_types_maybe;
352}
353
355template <uni20::MutableRankedStridedMdspecLike<2> SchurFormMdspan,
356 uni20::MutableRankedStridedMdspecLike<2> SchurVectorMdspan>
358KernelAttempt try_kernel(LapackBackend, schur_reorder_op const& operation, SchurFormMdspan& schur_form,
359 SchurVectorMdspan& schur_vectors)
360{
362 [&](auto& form_span, auto& vector_span) {
363 return lapack_detail::try_schur_reorder(operation, form_span, vector_span);
364 },
365 schur_form, schur_vectors);
366}
367
368} // namespace uni20::linalg
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
Operation-tag backend dispatch over tensor-view operands.
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 gees(char jobvs, char sort, blas_int n, Scalar *a, blas_int lda, blas_int &selected_dimension, Scalar *wr, Scalar *wi, Scalar *vs, blas_int ldvs, Scalar *work, blas_int lwork, blas_int *bwork)
Compute the real Schur form of a real nonsymmetric matrix.
Definition lapack.hpp:846
void trexc(char compq, blas_int n, Scalar *t, blas_int ldt, Scalar *q, blas_int ldq, blas_int &first, blas_int &last, Scalar *work)
Reorder adjacent blocks in a real Schur form.
Definition lapack.hpp:889
void hseqr(char job, char compz, blas_int n, blas_int first, blas_int last, Scalar *h, blas_int ldh, Scalar *wr, Scalar *wi, Scalar *z, blas_int ldz, Scalar *work, blas_int lwork)
Compute a Schur form of a real upper Hessenberg matrix.
Definition lapack.hpp:876
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
KernelAttempt try_hessenberg_schur(hessenberg_schur_op const &op, MatrixMdspan &hessenberg, std::span< EigenScalar > eigenvalues, SchurVectorMdspan &schur_vectors)
Compute a real upper-Hessenberg Schur form through LAPACK hseqr.
Definition backends/lapack/schur.hpp:161
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
consteval auto schur_acceptance()
Report compile-time eligibility for resolved LAPACK Schur decomposition.
Definition backends/lapack/schur.hpp:35
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_schur(schur_op const &op, MatrixMdspan &matrix_work, std::span< EigenScalar > eigenvalues, SchurVectorMdspan &schur_vectors)
Compute a dense Schur form through LAPACK gees.
Definition backends/lapack/schur.hpp:56
consteval auto hessenberg_schur_acceptance()
Report compile-time eligibility for resolved real upper-Hessenberg Schur decomposition.
Definition backends/lapack/schur.hpp:142
consteval auto schur_reorder_acceptance()
Report compile-time eligibility for resolved LAPACK Schur reordering.
Definition backends/lapack/schur.hpp:220
KernelAttempt try_schur_reorder(schur_reorder_op const &op, SchurFormMdspan &schur_form, SchurVectorMdspan &schur_vectors)
Move one real Schur block or complex Schur entry through LAPACK trexc.
Definition backends/lapack/schur.hpp:239
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
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
Schur decomposition of an already upper-Hessenberg real matrix.
Definition operation_tags.hpp:322
bool compute_vectors
Definition operation_tags.hpp:324
Dense Schur decomposition operation tag.
Definition operation_tags.hpp:315
bool compute_vectors
Definition operation_tags.hpp:317
Move one Schur block or entry to a requested position.
Definition operation_tags.hpp:329
std::size_t to
Definition operation_tags.hpp:332
bool update_vectors
Definition operation_tags.hpp:333
std::size_t from
Definition operation_tags.hpp:331
Trait to extract the scalar type from a type T.
Definition scalar_traits.hpp:93
Concepts for tensor-level objects that resolve mdspan kernel operands.
#define CHECK(cond,...)
Definition trace.hpp:299
#define CHECK_EQUAL(a, b,...)
Definition trace.hpp:309