Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
ops/self_adjoint_eigh.hpp
Go to the documentation of this file.
1#pragma once
2
19#include <uni20/tensor/copy.hpp>
22
23#include <array>
24#include <concepts>
25#include <type_traits>
26#include <utility>
27
28namespace uni20::linalg
29{
30namespace detail
31{
32using self_adjoint_eigenvalue_extents = stdex::dextents<uni20::index_type, 1>;
33
34template <uni20::MutableRankedTensorView<1> EigenvalueTensor, uni20::MutableRankedTensorView<2> MatrixTensor>
35[[nodiscard]] self_adjoint_eigh_op prepare_self_adjoint_eigh(EigenvalueTensor& eigenvalues, MatrixTensor& matrix_work,
36 SelfAdjointEighOptions options)
37{
38 ERROR_IF(matrix_work.extent(0) != matrix_work.extent(1), "self-adjoint eigensystem requires a square matrix");
39 uni20::prepare_output(eigenvalues,
40 self_adjoint_eigenvalue_extents{static_cast<uni20::index_type>(matrix_work.extent(0))});
41 return {.compute_vectors = options.compute_vectors, .triangle = options.triangle};
42}
43
44template <KernelBackendSelector BackendSelector, uni20::MutableRankedTensorView<1> EigenvalueTensor,
45 uni20::MutableRankedTensorView<2> MatrixTensor>
46void dispatch_self_adjoint_eigh(BackendSelector&& selector, self_adjoint_eigh_op operation,
47 EigenvalueTensor& eigenvalues, MatrixTensor& matrix_work, Precision p)
48{
49 auto eigenvalue_descriptor = uni20::mdspec_of(eigenvalues);
50 auto matrix_descriptor = uni20::mdspec_of(matrix_work);
51 detail::dispatch_at_precision<uni20::tensor_element_t<MatrixTensor>>(
52 std::forward<BackendSelector>(selector), operation, p, eigenvalue_descriptor, matrix_descriptor);
53
54 uni20::detail::record_output_precision(eigenvalues, p);
55 uni20::detail::record_output_precision(matrix_work, p);
56}
57} // namespace detail
58
65template <KernelBackendSelector BackendSelector, uni20::MutableRankedTensorView<1> EigenvalueTensor,
66 uni20::MutableRankedTensorView<2> MatrixTensor>
67void self_adjoint_eigh(BackendSelector&& selector, EigenvalueTensor&& eigenvalues, MatrixTensor&& matrix_work,
68 SelfAdjointEighOptions options = {}, std::optional<Precision> requested = {})
69{
70 auto const p = detail::decomposition_precision(matrix_work, requested);
71 auto operation = detail::prepare_self_adjoint_eigh(eigenvalues, matrix_work, options);
72 detail::dispatch_self_adjoint_eigh(std::forward<BackendSelector>(selector), operation, eigenvalues, matrix_work, p);
73}
74
76template <uni20::MutableRankedTensorView<1> EigenvalueTensor, uni20::MutableRankedTensorView<2> MatrixTensor>
77void self_adjoint_eigh(EigenvalueTensor&& eigenvalues, MatrixTensor&& matrix_work, SelfAdjointEighOptions options = {},
78 std::optional<Precision> requested = {})
79{
80 auto const p = detail::decomposition_precision(matrix_work, requested);
81 auto operation = detail::prepare_self_adjoint_eigh(eigenvalues, matrix_work, options);
82 auto selector = select_backend(operation, eigenvalues, matrix_work);
83 detail::dispatch_self_adjoint_eigh(selector, operation, eigenvalues, matrix_work, p);
84}
85
87template <class EigenvalueTensor, class EigenvectorTensor> struct SelfAdjointEighResult
88{
89 using eigenvalue_tensor_type = EigenvalueTensor;
90 using eigenvector_tensor_type = EigenvectorTensor;
91
92 EigenvalueTensor eigenvalues;
93 EigenvectorTensor eigenvectors;
94};
95
96namespace detail
97{
98template <class MatrixTensor>
99using self_adjoint_eigh_reuse_matrix_t =
100 std::conditional_t<std::same_as<typename std::remove_cvref_t<MatrixTensor>::layout_type, uni20::ColumnMajor>,
101 std::remove_cvref_t<MatrixTensor>,
102 typename std::remove_cvref_t<MatrixTensor>::template rebind_layout_type<stdex::layout_stride>>;
103
104template <class MatrixTensor> consteval bool can_transfer_self_adjoint_eigh_storage()
105{
106 using matrix_type = std::remove_cvref_t<MatrixTensor>;
107 if constexpr (requires {
108 typename matrix_type::storage_policy;
109 typename matrix_type::layout_type;
110 typename matrix_type::accessor_factory_type;
111 typename matrix_type::storage_type;
112 typename matrix_type::template rebind_layout_type<stdex::layout_stride>;
113 })
114 {
115 using work_type = self_adjoint_eigh_reuse_matrix_t<matrix_type>;
116 return std::same_as<typename matrix_type::storage_policy, uni20::HostStorage> &&
117 std::same_as<typename matrix_type::accessor_factory_type, uni20::DefaultAccessorFactory> &&
119 requires(matrix_type& matrix, typename work_type::mapping_type mapping,
120 typename work_type::storage_type storage) {
121 { std::move(matrix).release_storage() } -> std::same_as<typename work_type::storage_type>;
122 { work_type::adopt_storage(std::move(mapping), std::move(storage)) } -> std::same_as<work_type>;
123 };
124 }
125 else
126 {
127 return false;
128 }
129}
130
131constexpr MatrixTriangle transposed_triangle(MatrixTriangle triangle)
132{
133 switch (triangle)
134 {
139 }
140 PANIC("invalid MatrixTriangle", std::to_underlying(triangle));
141}
142
143template <class WorkMatrix, uni20::RankedTensorView<2> MatrixTensor>
144[[nodiscard]] WorkMatrix materialize_self_adjoint_eigh_work_matrix(MatrixTensor const& matrix)
145{
146 WorkMatrix result = [&] {
147 if constexpr (std::same_as<typename WorkMatrix::layout_type, uni20::ColumnMajor>)
148 {
149 return WorkMatrix(matrix.extents());
150 }
151 else
152 {
153 return WorkMatrix(matrix.extents(), uni20::layout::LayoutLeft{});
154 }
155 }();
156 uni20::copy(result, matrix);
157 return result;
158}
159
160template <class WorkMatrix, uni20::RankedImmediateTensorView<2> MatrixTensor>
161[[nodiscard]] auto column_major_reuse_mapping(MatrixTensor const& matrix, uni20::blas_int leading_dimension) ->
162 typename WorkMatrix::mapping_type
163{
164 if constexpr (std::same_as<typename WorkMatrix::layout_type, uni20::ColumnMajor>)
165 {
166 return typename WorkMatrix::mapping_type(matrix.extents());
167 }
168 else
169 {
170 using index_type = typename WorkMatrix::index_type;
171 return typename WorkMatrix::mapping_type(
172 matrix.extents(), std::array<index_type, 2>{index_type{1}, static_cast<index_type>(leading_dimension)});
173 }
174}
175
176template <class MatrixTensor>
178[[nodiscard]] constexpr auto select_self_adjoint_eigh_backend(MatrixTriangle triangle)
179{
181 using eigenvalue_tensor = uni20::Tensor<real_type, 1>;
182 return select_backend_for<eigenvalue_tensor, std::remove_cvref_t<MatrixTensor>>(
183 self_adjoint_eigh_op{.compute_vectors = true, .triangle = triangle});
184}
185
186template <KernelBackendSelector BackendSelector, uni20::OwningTensor MatrixTensor>
188[[nodiscard]] auto eigh_from_work_matrix(BackendSelector&& selector, MatrixTensor matrix_work, MatrixTriangle triangle,
189 Precision p)
190{
192 uni20::Tensor<real_type, 1> eigenvalues;
193 self_adjoint_eigh(std::forward<BackendSelector>(selector), eigenvalues, matrix_work,
194 SelfAdjointEighOptions{.compute_vectors = true, .triangle = triangle}, p);
195 return SelfAdjointEighResult<decltype(eigenvalues), MatrixTensor>{.eigenvalues = std::move(eigenvalues),
196 .eigenvectors = std::move(matrix_work)};
197}
198
199template <KernelBackendSelector BackendSelector, uni20::OwningTensor MatrixTensor>
201[[nodiscard]] auto eigh_from_consumed_matrix(BackendSelector&& selector, MatrixTensor&& matrix, MatrixTriangle triangle,
202 Precision p)
203{
204 using matrix_type = std::remove_cvref_t<MatrixTensor>;
205 using work_type = self_adjoint_eigh_reuse_matrix_t<matrix_type>;
206
207 auto const matrix_span = matrix.mdspan();
208 auto const stage = uni20::linalg::blas::try_mdspan_matrix_stage(matrix_span);
209 if (stage && !stage->needs_conjugation)
210 {
211 bool const transpose_storage = stage->unit_stride_axis == 1;
212 MatrixTriangle const work_triangle = transpose_storage ? transposed_triangle(triangle) : triangle;
213 auto work_mapping = column_major_reuse_mapping<work_type>(matrix, stage->nonunit_stride);
214 auto storage = std::move(matrix).release_storage();
215 auto work_matrix = work_type::adopt_storage(std::move(work_mapping), std::move(storage));
216 auto result =
217 eigh_from_work_matrix(std::forward<BackendSelector>(selector), std::move(work_matrix), work_triangle, p);
219 {
220 if (transpose_storage) uni20::conjugate_inplace(result.eigenvectors);
221 }
222 return result;
223 }
224
225 auto work_matrix = materialize_self_adjoint_eigh_work_matrix<work_type>(matrix);
226 return eigh_from_work_matrix(std::forward<BackendSelector>(selector), std::move(work_matrix), triangle, p);
227}
228} // namespace detail
229
233template <KernelBackendSelector BackendSelector, uni20::RankedTensorView<2> MatrixTensor>
234[[nodiscard]] auto eigh(BackendSelector&& selector, MatrixTensor const& matrix,
235 MatrixTriangle triangle = MatrixTriangle::Upper, std::optional<Precision> requested = {})
236{
237 auto const p = detail::decomposition_precision(matrix, requested);
238 ERROR_IF(matrix.extent(0) != matrix.extent(1), "self-adjoint eigensystem requires a square matrix");
239 auto matrix_work = uni20::make_tensor<uni20::ColumnMajor>(matrix);
240 return detail::eigh_from_work_matrix(std::forward<BackendSelector>(selector), std::move(matrix_work), triangle, p);
241}
242
247template <uni20::RankedTensorView<2> MatrixTensor>
248[[nodiscard]] auto eigh(MatrixTensor const& matrix, MatrixTriangle triangle = MatrixTriangle::Upper,
249 std::optional<Precision> requested = {})
250{
251 auto const p = detail::decomposition_precision(matrix, requested);
252 ERROR_IF(matrix.extent(0) != matrix.extent(1), "self-adjoint eigensystem requires a square matrix");
253 auto matrix_work = uni20::make_tensor<uni20::ColumnMajor>(matrix);
254 auto selector = detail::select_self_adjoint_eigh_backend<decltype(matrix_work)>(triangle);
255 return detail::eigh_from_work_matrix(std::move(selector), std::move(matrix_work), triangle, p);
256}
257
262template <KernelBackendSelector BackendSelector, class MatrixTensor>
264 (!std::is_lvalue_reference_v<MatrixTensor>) && (!std::is_const_v<std::remove_reference_t<MatrixTensor>>)
265[[nodiscard]] auto eigh(BackendSelector&& selector, MatrixTensor&& matrix,
266 MatrixTriangle triangle = MatrixTriangle::Upper, std::optional<Precision> requested = {})
267{
268 auto const p = detail::decomposition_precision(matrix, requested);
269 ERROR_IF(matrix.extent(0) != matrix.extent(1), "self-adjoint eigensystem requires a square matrix");
270 if constexpr (detail::can_transfer_self_adjoint_eigh_storage<MatrixTensor>())
271 {
272 return detail::eigh_from_consumed_matrix(std::forward<BackendSelector>(selector),
273 std::forward<MatrixTensor>(matrix), triangle, p);
274 }
275 else
276 {
277 auto matrix_work = uni20::make_tensor<uni20::ColumnMajor>(matrix);
278 return detail::eigh_from_work_matrix(std::forward<BackendSelector>(selector), std::move(matrix_work), triangle, p);
279 }
280}
281
293template <class MatrixTensor>
295 (!std::is_lvalue_reference_v<MatrixTensor>) && (!std::is_const_v<std::remove_reference_t<MatrixTensor>>)
296[[nodiscard]] auto eigh(MatrixTensor&& matrix, MatrixTriangle triangle = MatrixTriangle::Upper,
297 std::optional<Precision> requested = {})
298{
299 auto const p = detail::decomposition_precision(matrix, requested);
300 ERROR_IF(matrix.extent(0) != matrix.extent(1), "self-adjoint eigensystem requires a square matrix");
301 if constexpr (detail::can_transfer_self_adjoint_eigh_storage<MatrixTensor>())
302 {
303 using work_type = detail::self_adjoint_eigh_reuse_matrix_t<std::remove_cvref_t<MatrixTensor>>;
304 auto selector = detail::select_self_adjoint_eigh_backend<work_type>(triangle);
305 return detail::eigh_from_consumed_matrix(std::move(selector), std::forward<MatrixTensor>(matrix), triangle, p);
306 }
307 else
308 {
309 auto matrix_work = uni20::make_tensor<uni20::ColumnMajor>(matrix);
310 auto selector = detail::select_self_adjoint_eigh_backend<decltype(matrix_work)>(triangle);
311 return detail::eigh_from_work_matrix(std::move(selector), std::move(matrix_work), triangle, p);
312 }
313}
314
316template <RankedTensorView<2> Matrix>
317[[nodiscard]] auto eigh(Matrix&& matrix, Precision p, MatrixTriangle triangle = MatrixTriangle::Upper)
318{
319 return uni20::linalg::eigh(std::forward<Matrix>(matrix), triangle, std::optional<Precision>{p});
320}
321
323template <KernelBackendSelector Selector, RankedTensorView<2> Matrix>
324[[nodiscard]] auto eigh(Selector&& selector, Matrix&& matrix, Precision p,
325 MatrixTriangle triangle = MatrixTriangle::Upper)
326{
327 return uni20::linalg::eigh(std::forward<Selector>(selector), std::forward<Matrix>(matrix), triangle,
328 std::optional<Precision>{p});
329}
330
332template <MutableRankedTensorView<1> V, MutableRankedTensorView<2> A>
333void self_adjoint_eigh(V&& eigenvalues, A&& matrix_work, Precision p, SelfAdjointEighOptions options = {})
334{
335 uni20::linalg::self_adjoint_eigh(std::forward<V>(eigenvalues), std::forward<A>(matrix_work), options,
336 std::optional<Precision>{p});
337}
338
340template <KernelBackendSelector Selector, MutableRankedTensorView<1> V, MutableRankedTensorView<2> A>
341void self_adjoint_eigh(Selector&& selector, V&& eigenvalues, A&& matrix_work, Precision p,
342 SelfAdjointEighOptions options = {})
343{
344 uni20::linalg::self_adjoint_eigh(std::forward<Selector>(selector), std::forward<V>(eigenvalues),
345 std::forward<A>(matrix_work), options, std::optional<Precision>{p});
346}
347
348} // namespace uni20::linalg
LAPACK backend for dense symmetric and Hermitian eigensystems.
Exact arithmetic or an explicit binary working precision.
Definition core/precision.hpp:13
General-purpose owning tensor with immediate or deferred mdspan metadata.
Definition basic_tensor.hpp:109
Concept for complex scalar types.
Definition scalar_concepts.hpp:75
An mdspan-like type whose accessor is stdex::default_accessor.
Definition mdspan/concepts.hpp:501
MutableImmediateTensorView whose writable mdspan has a specified static rank.
Definition tensor/concepts.hpp:209
Tensor-level object that owns the storage described by its multidimensional view.
Definition tensor/concepts.hpp:116
TensorView whose readable mdspec has a specified static rank.
Definition tensor/concepts.hpp:149
Operation-tag backend dispatch over tensor-view operands.
typename make_real< T >::type make_real_t
Alias for the underlying real-valued type of T.
Definition scalar_traits.hpp:168
Lower mdspan matrix views to provider-ready BLAS operands.
auto try_mdspan_matrix_stage(Mdspan const &span) -> std::optional< MdspanMatrixStage< std::remove_cv_t< typename Mdspan::element_type >, detail::span_data_t< Mdspan > > >
Build a matrix staging descriptor when direct provider lowering is possible.
Definition mdspan_matrix.hpp:202
Definition linalg/async.hpp:28
auto eigh(BackendSelector selector, async::Async< MatrixTensor > const &matrix, MatrixTriangle triangle=MatrixTriangle::Upper, std::optional< Precision > requested={})
Schedule a preserving self-adjoint eigensystem through an explicit selector.
Definition async/self_adjoint_eigh.hpp:122
MatrixTriangle
Triangle of a symmetric or Hermitian matrix supplied to an operation.
Definition operation_tags.hpp:226
void self_adjoint_eigh(BackendSelector &&selector, EigenvalueTensor &&eigenvalues, MatrixTensor &&matrix_work, SelfAdjointEighOptions options={}, std::optional< Precision > requested={})
Compute an in-place self-adjoint eigensystem through an explicit selector.
Definition ops/self_adjoint_eigh.hpp:67
constexpr auto select_backend(Operation const &operation, FirstTensor const &, RestTensors const &...)
Select the default backend list for an operation on Tensor operands.
Definition backend_selector.hpp:252
Definition data_table.cpp:8
Output & prepare_output(Output &output, RequiredExtents const &required)
Prepare a mutable tensor output with the specified shape.
Definition tensor/output.hpp:104
stdex::layout_left ColumnMajor
Conventional column-major contiguous mdspan layout.
Definition tensor.hpp:33
void conjugate_inplace(BackendSelector selector, async::Async< Tensor > &tensor)
Schedule eager in-place conjugation through an explicit selector.
Definition linalg/async/conjugate_inplace.hpp:44
constexpr decltype(auto) mdspec_of(Tensor &tensor) noexcept(noexcept(tensor.mdspec()))
Return a tensor's immediate or descriptor-backed multidimensional metadata.
Definition tensor/concepts.hpp:50
void copy(BackendSelector selector, async::Async< OutputTensor > &output, async::Async< InputTensor > const &input)
Schedule a Tensor copy through an explicit backend selector.
Definition linalg/async/copy.hpp:174
std::ptrdiff_t index_type
Definition types.hpp:19
Backend-independent operation values used with normalized kernel operands.
Definition layout.hpp:64
Options for a dense symmetric or Hermitian eigensystem.
Definition operation_tags.hpp:233
Owning eigenvalues and eigenvectors returned by eigh.
Definition ops/self_adjoint_eigh.hpp:88
EigenvalueTensor eigenvalues
Definition ops/self_adjoint_eigh.hpp:92
EigenvalueTensor eigenvalue_tensor_type
Definition ops/self_adjoint_eigh.hpp:89
EigenvectorTensor eigenvector_tensor_type
Definition ops/self_adjoint_eigh.hpp:90
EigenvectorTensor eigenvectors
Definition ops/self_adjoint_eigh.hpp:93
Backend-dispatched in-place conjugation of tensor elements.
Backend-dispatched tensor copies and inferred owning materialization.
Shape preparation contracts for tensor operation outputs.
General-purpose owning tensors with runtime extents.
#define PANIC(...)
Definition trace.hpp:382
#define ERROR_IF(cond,...)
Definition trace.hpp:389