Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
decompositions_impl.hpp
Go to the documentation of this file.
1#pragma once
2
10#if defined(MPLAPACK_BUILD_WITH_MPFR)
11#include <mplapack_mpfr.h>
12#endif
13#if defined(MPLAPACK_BUILD_WITH_BINARY80)
14#include <mplapack_binary80.h>
15#endif
16
17#include <algorithm>
18#include <limits>
19#include <span>
23#include <vector>
24
25namespace uni20::mplapack::detail
26{
27inline std::size_t auxiliary_size(std::size_t count, std::size_t factor)
28{
29 if (count > std::numeric_limits<std::size_t>::max() / factor)
30 throw std::length_error("MPLAPACK auxiliary workspace size overflow");
31 return std::max<std::size_t>(1, count * factor);
32}
33
34template <class Adapter, class Scalar>
35void orthogonal_factor_impl(bool lq, std::size_t m, std::size_t n, std::span<Scalar> a, std::span<Scalar> q,
36 std::span<Scalar> triangular, Precision p)
37{
38 auto k = std::min(m, n);
39 Adapter::check_size(a.size(), m, n);
40 Adapter::check_size(q.size(), lq ? k : m, lq ? n : k);
41 Adapter::check_size(triangular.size(), lq ? m : k, lq ? k : n);
42 auto pm = Adapter::integer(m), pn = Adapter::integer(n), pk = Adapter::integer(k);
43 typename Adapter::precision_scope scope(p);
44 if (!k) return;
45 using P = typename Adapter::template scalar<Scalar>;
46 auto av = Adapter::pack_values(std::span<Scalar const>(a));
47 std::vector<P> tau(k);
48 Adapter::template workspace<P>(lq ? "gelqf" : "geqrf", [&](P* work, mplapackint length, mplapackint& info) {
49 if constexpr (Real<Scalar>)
50 {
51 if (lq)
52 Rgelqf(pm, pn, av.data(), pm, tau.data(), work, length, info);
53 else
54 Rgeqrf(pm, pn, av.data(), pm, tau.data(), work, length, info);
55 }
56 else
57 {
58 if (lq)
59 Cgelqf(pm, pn, av.data(), pm, tau.data(), work, length, info);
60 else
61 Cgeqrf(pm, pn, av.data(), pm, tau.data(), work, length, info);
62 }
63 });
64 auto tr = lq ? m : k, tc = lq ? k : n;
65 for (std::size_t j = 0; j < tc; ++j)
66 for (std::size_t i = 0; i < tr; ++i)
67 triangular[i + j * tr] =
68 (lq ? i >= j : i <= j) ? Adapter::get_value(av[i + j * m], p) : scalar_at_precision<Scalar>(0, p);
69 Adapter::template workspace<P>(lq ? "orglq" : "orgqr", [&](P* work, mplapackint length, mplapackint& info) {
70 if constexpr (Real<Scalar>)
71 {
72 if (lq)
73 Rorglq(pk, pn, pk, av.data(), pm, tau.data(), work, length, info);
74 else
75 Rorgqr(pm, pk, pk, av.data(), pm, tau.data(), work, length, info);
76 }
77 else
78 {
79 if (lq)
80 Cunglq(pk, pn, pk, av.data(), pm, tau.data(), work, length, info);
81 else
82 Cungqr(pm, pk, pk, av.data(), pm, tau.data(), work, length, info);
83 }
84 });
85 auto qr = lq ? k : m, qc = lq ? n : k;
86 for (std::size_t j = 0; j < qc; ++j)
87 for (std::size_t i = 0; i < qr; ++i)
88 q[i + j * qr] = Adapter::get_value(av[i + j * m], p);
89 Adapter::unpack_values(av, a, p);
90}
91
92template <class Adapter, class Scalar>
93void eigh_impl(std::size_t n, std::span<Scalar> a, std::span<make_real_t<Scalar>> values, bool vectors, char triangle,
94 Precision p)
95{
96 Adapter::check_size(a.size(), n, n);
97 Adapter::check_size(values.size(), n, 1);
98 if (triangle != 'U' && triangle != 'L') throw std::invalid_argument("invalid eigensystem triangle");
99 auto pn = Adapter::integer(n);
100 typename Adapter::precision_scope scope(p);
101 if (!n) return;
102 using P = typename Adapter::template scalar<Scalar>;
103 auto av = Adapter::pack_values(std::span<Scalar const>(a));
104 std::vector<typename Adapter::real> w(n);
105 char job = vectors ? 'V' : 'N';
106 std::vector<typename Adapter::real> rwork;
107 if constexpr (!Real<Scalar>) rwork.resize(auxiliary_size(n, 3));
108 Adapter::template workspace<P>("eigh", [&](P* work, mplapackint length, mplapackint& info) {
109 if constexpr (Real<Scalar>)
110 Rsyev(&job, &triangle, pn, av.data(), pn, w.data(), work, length, info);
111 else
112 Cheev(&job, &triangle, pn, av.data(), pn, w.data(), work, length, rwork.data(), info);
113 });
114 Adapter::unpack_values(av, a, p);
115 Adapter::unpack_values(w, values, p);
116}
117
118template <class Adapter, class Scalar>
119void svd_impl(std::size_t m, std::size_t n, std::span<Scalar> a, std::span<make_real_t<Scalar>> values,
120 std::span<Scalar> u, std::span<Scalar> vh, char jobu, char jobvt, Precision p)
121{
122 auto k = std::min(m, n);
123 auto valid_job = [](char job) { return job == 'A' || job == 'S' || job == 'N' || job == 'O'; };
124 if (!valid_job(jobu) || !valid_job(jobvt) || (jobu == 'O' && jobvt == 'O'))
125 throw std::invalid_argument("invalid SVD vector request");
126 auto uc = jobu == 'A' ? m : jobu == 'S' ? k : 0;
127 auto vr = jobvt == 'A' ? n : jobvt == 'S' ? k : 0;
128 Adapter::check_size(a.size(), m, n);
129 Adapter::check_size(values.size(), k, 1);
130 Adapter::check_size(u.size(), m, uc);
131 Adapter::check_size(vh.size(), vr, n);
132 auto pm = Adapter::integer(m), pn = Adapter::integer(n);
133 auto ldu = std::max<mplapackint>(1, uc ? pm : 1);
134 auto ldvt = std::max<mplapackint>(1, Adapter::integer(vr));
135 typename Adapter::precision_scope scope(p);
136 if (!k)
137 {
138 for (std::size_t j = 0; j < uc; ++j)
139 for (std::size_t i = 0; i < m; ++i)
140 u[i + j * m] = scalar_at_precision<Scalar>(i == j ? 1 : 0, p);
141 for (std::size_t j = 0; j < n; ++j)
142 for (std::size_t i = 0; i < vr; ++i)
143 vh[i + j * vr] = scalar_at_precision<Scalar>(i == j ? 1 : 0, p);
144 return;
145 }
146 using P = typename Adapter::template scalar<Scalar>;
147 auto av = Adapter::pack_values(std::span<Scalar const>(a));
148 std::vector<P> uv(std::max<std::size_t>(1, u.size())), vv(std::max<std::size_t>(1, vh.size()));
149 std::vector<typename Adapter::real> w(k), rwork;
150 if constexpr (!Real<Scalar>) rwork.resize(auxiliary_size(k, 5));
151 Adapter::template workspace<P>("gesvd", [&](P* work, mplapackint length, mplapackint& info) {
152 if constexpr (Real<Scalar>)
153 Rgesvd(&jobu, &jobvt, pm, pn, av.data(), pm, w.data(), uv.data(), ldu, vv.data(), ldvt, work, length, info);
154 else
155 Cgesvd(&jobu, &jobvt, pm, pn, av.data(), pm, w.data(), uv.data(), ldu, vv.data(), ldvt, work, length,
156 rwork.data(), info);
157 });
158 Adapter::unpack_values(av, a, p);
159 Adapter::unpack_values(w, values, p);
160 uv.resize(u.size());
161 vv.resize(vh.size());
162 Adapter::unpack_values(uv, u, p);
163 Adapter::unpack_values(vv, vh, p);
164}
165
166template <class Adapter, class Scalar>
167void eigen_impl(std::size_t n, std::span<Scalar> a, std::span<complex<make_real_t<Scalar>>> values,
168 std::span<complex<make_real_t<Scalar>>> vectors, bool compute_vectors, Precision p)
169{
170 Adapter::check_size(a.size(), n, n);
171 Adapter::check_size(values.size(), n, 1);
172 Adapter::check_size(vectors.size(), compute_vectors ? n : 0, n);
173 auto pn = Adapter::integer(n);
174 typename Adapter::precision_scope scope(p);
175 if (!n) return;
176 using P = typename Adapter::template scalar<Scalar>;
177 auto av = Adapter::pack_values(std::span<Scalar const>(a));
178 std::vector<P> vl(1), vr(std::max<std::size_t>(1, vectors.size()));
179 char job = compute_vectors ? 'V' : 'N';
180 auto ldvr = compute_vectors ? pn : mplapackint{1};
181 if constexpr (Real<Scalar>)
182 {
183 std::vector<P> wr(n), wi(n);
184 Adapter::template workspace<P>("geev", [&](P* work, mplapackint length, mplapackint& info) {
185 Rgeev("N", &job, pn, av.data(), pn, wr.data(), wi.data(), vl.data(), 1, vr.data(), ldvr, work, length, info);
186 });
187 for (std::size_t j = 0; j < n; ++j)
188 values[j] = complex<make_real_t<Scalar>>(Adapter::get_value(wr[j], p), Adapter::get_value(wi[j], p));
189 if (compute_vectors)
190 for (std::size_t j = 0; j < n; ++j)
191 {
192 if (wi[j] == 0)
193 for (std::size_t i = 0; i < n; ++i)
194 vectors[i + j * n] = complex<make_real_t<Scalar>>(Adapter::get_value(vr[i + j * n], p),
195 scalar_at_precision<make_real_t<Scalar>>(0, p));
196 else
197 {
198 CHECK(wi[j] > 0 && j + 1 < n, "invalid conjugate eigenvector pair");
199 for (std::size_t i = 0; i < n; ++i)
200 {
201 auto v = complex<make_real_t<Scalar>>(Adapter::get_value(vr[i + j * n], p),
202 Adapter::get_value(vr[i + (j + 1) * n], p));
203 vectors[i + j * n] = v;
204 vectors[i + (j + 1) * n] = uni20::conj(v);
205 }
206 ++j;
207 }
208 }
209 }
210 else
211 {
212 std::vector<P> w(n);
213 std::vector<typename Adapter::real> rwork(auxiliary_size(n, 2));
214 Adapter::template workspace<P>("geev", [&](P* work, mplapackint length, mplapackint& info) {
215 Cgeev("N", &job, pn, av.data(), pn, w.data(), vl.data(), 1, vr.data(), ldvr, work, length, rwork.data(), info);
216 });
217 Adapter::unpack_values(w, values, p);
218 if (compute_vectors) Adapter::unpack_values(vr, vectors, p);
219 }
220 Adapter::unpack_values(av, a, p);
221}
222
223} // namespace uni20::mplapack::detail
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
void info(checked_format_string< Args... > format, Args &&... args)
Emit an informational display message.
Definition display.hpp:393
auto values(Tensor &tensor)
Definition packed_matrix.hpp:29
void lq(std::size_t m, std::size_t n, std::span< float80 > a, std::span< float80 > q, std::span< float80 > triangular, Precision p)
Overwrite m-by-n input work and return Q (k-by-n) and L (m-by-k), k=min(m,n).
Definition binary80.cpp:231
void qr(std::size_t m, std::size_t n, std::span< float80 > a, std::span< float80 > q, std::span< float80 > triangular, Precision p)
Overwrite m-by-n input work and return Q (m-by-k) and R (k-by-n), k=min(m,n).
Definition binary80.cpp:225
Definition data_table.cpp:8
constexpr S scalar_at_precision(V const &value, Precision p)
Construct or convert a scalar at one explicit precision.
Definition runtime_precision.hpp:76
#define CHECK(cond,...)
Definition trace.hpp:299