NumericAL
A header-only C++20 lexer built on REAL
Loading...
Searching...
No Matches
eigen.hpp
Go to the documentation of this file.
1
17#ifndef NUMERICAL_EIGEN_HPP
18#define NUMERICAL_EIGEN_HPP
19
20#include <algorithm>
21#include <array>
22#include <cmath>
23#include <complex>
24#include <cstddef>
25#include <exception>
26#include <limits>
27#include <type_traits>
28#include <utility>
29#include <vector>
30
31#include "decomposition.hpp"
32
33namespace numerical {
34
35 namespace detail {
36
38 inline constexpr double schur_ulp {std::numeric_limits<double>::epsilon()};
39
41 inline constexpr std::size_t schur_steps_per_order {30};
42
46 inline constexpr double balance_limit {0x1p256};
47
50 inline constexpr double back_substitution_limit {0x1p400};
51
53 [[nodiscard]] inline double modulus_1(double x)
54 {
55 return std::abs(x);
56 }
57
59 [[nodiscard]] inline double modulus_1(const std::complex<double>& x)
60 {
61 return std::abs(x.real()) + std::abs(x.imag());
62 }
63
65 template <typename A,
66 typename B>
67 [[nodiscard]] auto times(const A& a,
68 const B& b)
69 {
70 return a * b;
71 }
72
76 [[nodiscard]] inline std::complex<double> times(const std::complex<double>& a,
77 const std::complex<double>& b)
78 {
79 return {(a.real() * b.real()) - (a.imag() * b.imag()), (a.real() * b.imag()) + (a.imag() * b.real())};
80 }
81
83 [[nodiscard]] inline double largest_part(double x)
84 {
85 return std::abs(x);
86 }
87
89 [[nodiscard]] inline double largest_part(const std::complex<double>& x)
90 {
91 return std::max(std::abs(x.real()), std::abs(x.imag()));
92 }
93
96 struct balancing
97 {
98 std::size_t low {0};
99 std::size_t high {0};
100 std::vector<std::pair<std::size_t, std::size_t>> swaps;
102 };
103
105 template <typename T>
107 std::size_t n,
108 std::size_t i,
109 std::size_t j)
110 {
111 for (std::size_t c {0}; c < n; ++c) {
112 std::swap(a[(i * n) + c], a[(j * n) + c]);
113 }
114 for (std::size_t r {0}; r < n; ++r) {
115 std::swap(a[(r * n) + i], a[(r * n) + j]);
116 }
117 }
118
125 template <typename T>
127 std::size_t n)
128 {
130 out.scale = buffer<double>(n, 1.0);
131 out.high = n;
132 for (bool moved {true}; moved;) {
133 moved = false;
134 for (std::size_t i {out.high}; i-- > out.low;) {
135 bool alone {true};
136 for (std::size_t j {out.low}; j < out.high && alone; ++j) {
137 alone = j == i || a[(i * n) + j] == T {};
138 }
139 if (alone) {
140 exchange(a, n, i, out.high - 1);
141 out.swaps.emplace_back(i, out.high - 1);
142 --out.high;
143 moved = true;
144 break;
145 }
146 }
147 }
148 for (bool moved {true}; moved;) {
149 moved = false;
150 for (std::size_t j {out.low}; j < out.high; ++j) {
151 bool alone {true};
152 for (std::size_t i {out.low}; i < out.high && alone; ++i) {
153 alone = i == j || a[(i * n) + j] == T {};
154 }
155 if (alone) {
156 exchange(a, n, j, out.low);
157 out.swaps.emplace_back(j, out.low);
158 ++out.low;
159 moved = true;
160 break;
161 }
162 }
163 }
164 buffer<T> gathered(out.high - out.low);
165 for (bool unbalanced {true}; unbalanced;) {
166 unbalanced = false;
167 for (std::size_t i {out.low}; i < out.high; ++i) {
168 for (std::size_t k {out.low}; k < out.high; ++k) { // the column gathered: scaled_norm's sums run contiguous
169 gathered[k - out.low] = a[(k * n) + i];
170 }
171 double column {scaled_norm(gathered)};
172 double row {scaled_norm(a.data() + (i * n) + out.low, out.high - out.low)};
173 // Both are positive: the exchanges left every row and column of the block an off-diagonal entry in it.
174 const double before {column + row};
175 double f {1.0};
176 while (2.0 * column < row && out.scale[i] * f < balance_limit) {
177 f *= 2.0;
178 column *= 2.0;
179 row /= 2.0;
180 }
181 while (column >= 2.0 * row && out.scale[i] * f > 1.0 / balance_limit) {
182 f /= 2.0;
183 column /= 2.0;
184 row *= 2.0;
185 }
186 if (column + row >= 0.95 * before) {
187 continue;
188 }
189 out.scale[i] *= f;
190 unbalanced = true;
191 for (std::size_t k {out.low}; k < n; ++k) {
192 a[(i * n) + k] /= f;
193 }
194 for (std::size_t k {0}; k < out.high; ++k) {
195 a[(k * n) + i] *= f;
196 }
197 }
198 }
199 return out;
200 }
201
214 template <typename T>
216 std::size_t n,
217 std::size_t low,
218 std::size_t high,
219 bool form)
220 {
221 std::vector<buffer<T>> reflectors(n); // reflectors[k] acts on rows (and columns) k + 1 … high − 1
222 for (std::size_t k {low}; k + 2 < high; ++k) {
223 buffer<T> v(high - k - 1, T {});
224 for (std::size_t i {k + 1}; i < high; ++i) {
225 v[i - k - 1] = a[(i * n) + k];
226 }
227 if (scaled_norm(v.data() + 1, v.size() - 1) == 0.0) {
228 continue;
229 }
230 (void)rescale_tiny(v); // the same reflector at any scale
231 const double norm {scaled_norm(v)};
232 v[0] += phase_of(v[0]) * from_double<T>(norm);
234 for (T& element : v) {
235 element /= length;
236 }
237 buffer<T> w(n - k, T {}); // the rows times (I − 2 v vᴴ) from the left: w = vᴴ A over columns k …
238 for (std::size_t i {0}; i < v.size(); ++i) {
239 const T weight {conj_of(v[i])};
240 const T* row {a.data() + ((k + 1 + i) * n) + k};
241 for (std::size_t c {0}; c < w.size(); ++c) {
242 w[c] += times(weight, row[c]);
243 }
244 }
245 for (std::size_t i {0}; i < v.size(); ++i) {
246 const T twice {static_cast<T>(2) * v[i]};
247 T* row {a.data() + ((k + 1 + i) * n) + k};
248 for (std::size_t c {0}; c < w.size(); ++c) {
249 row[c] -= times(twice, w[c]);
250 }
251 }
252 for (std::size_t r {0}; r < high; ++r) { // A (I − 2 v vᴴ) on columns k + 1 …; rows below are zero there
253 T* row {a.data() + (r * n) + k + 1};
254 T s {};
255 for (std::size_t j {0}; j < v.size(); ++j) {
256 s += times(row[j], v[j]);
257 }
258 s *= static_cast<T>(2);
259 for (std::size_t j {0}; j < v.size(); ++j) {
260 row[j] -= times(s, conj_of(v[j]));
261 }
262 }
263 for (std::size_t i {k + 2}; i < high; ++i) {
264 a[(i * n) + k] = T {};
265 }
266 reflectors[k] = std::move(v);
267 }
268 if (!form) {
269 return {};
270 }
271 buffer<T> q(n * n, T {});
272 for (std::size_t i {0}; i < n; ++i) {
273 q[(i * n) + i] = T {1};
274 }
275 for (std::size_t k {n}; k-- > 0;) { // Q = P_low … P_high−3, accumulated backward: each acts on rows k + 1 …
276 const buffer<T>& v {reflectors[k]};
277 if (v.empty()) {
278 continue;
279 }
280 buffer<T> w(v.size(), T {});
281 for (std::size_t i {0}; i < v.size(); ++i) {
282 const T weight {conj_of(v[i])};
283 const T* row {q.data() + ((k + 1 + i) * n) + k + 1};
284 for (std::size_t c {0}; c < w.size(); ++c) {
285 w[c] += times(weight, row[c]);
286 }
287 }
288 for (std::size_t i {0}; i < v.size(); ++i) {
289 const T twice {static_cast<T>(2) * v[i]};
290 T* row {q.data() + ((k + 1 + i) * n) + k + 1};
291 for (std::size_t c {0}; c < w.size(); ++c) {
292 row[c] -= times(twice, w[c]);
293 }
294 }
295 }
296 buffer<T> qt(n * n, T {});
297 for (std::size_t i {0}; i < n; ++i) {
298 for (std::size_t j {0}; j < n; ++j) {
299 qt[(j * n) + i] = q[(i * n) + j];
300 }
301 }
302 return qt;
303 }
304
306 inline constexpr std::size_t hessenberg_block {32};
307
309 inline constexpr std::size_t hessenberg_crossover {128};
310
312 [[nodiscard]] inline bool already_reduced(double /*alpha*/,
313 double rest)
314 {
315 return rest == 0.0;
316 }
317
319 [[nodiscard]] inline bool already_reduced(const std::complex<double>& alpha,
320 double rest)
321 {
322 return rest == 0.0 && alpha.imag() == 0.0;
323 }
324
330 template <typename T>
332 T* x,
333 std::size_t count)
334 {
335 const double rest {scaled_norm(x, count)};
336 if (already_reduced(alpha, rest)) {
337 return T {};
338 }
339 const double beta {-std::copysign(std::hypot(static_cast<double>(std::abs(alpha)), rest), real_part(alpha))};
340 const T tau {(from_double<T>(beta) - alpha) / from_double<T>(beta)};
341 const T scale {T {1} / (alpha - from_double<T>(beta))};
342 for (std::size_t i {0}; i < count; ++i) {
343 x[i] *= scale;
344 }
345 alpha = from_double<T>(beta);
346 return tau;
347 }
348
351 {
353
355 [[nodiscard]] double dot(const double* row) const
356 {
357 return dot_product(row, v.data(), v.size());
358 }
359 };
360
365 {
368
370 [[nodiscard]] std::complex<double> dot(const std::complex<double>* row) const
371 {
372 const auto* const parts {reinterpret_cast<const double*>(row)}; // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast): the layout guarantee above
373 return {dot_product(parts, real_side.data(), real_side.size()), dot_product(parts, imaginary_side.data(), imaginary_side.size())};
374 }
375 };
376
378 [[nodiscard]] inline real_operand operand_of(const double* v,
379 std::size_t stride,
380 std::size_t count)
381 {
383 for (std::size_t l {0}; l < count; ++l) {
384 out.v[l] = v[l * stride];
385 }
386 return out;
387 }
388
390 [[nodiscard]] inline complex_operand operand_of(const std::complex<double>* v,
391 std::size_t stride,
392 std::size_t count)
393 {
394 complex_operand out {buffer<double>(2 * count), buffer<double>(2 * count)};
395 for (std::size_t l {0}; l < count; ++l) {
396 const std::complex<double> x {v[l * stride]};
397 out.real_side[2 * l] = x.real();
398 out.real_side[(2 * l) + 1] = -x.imag();
399 out.imaginary_side[2 * l] = x.imag();
400 out.imaginary_side[(2 * l) + 1] = x.real();
401 }
402 return out;
403 }
404
406 template <typename T>
408 {
409 std::size_t first {0};
410 std::size_t width {0};
413 };
414
421 template <typename T>
423 std::size_t n,
424 std::size_t low,
425 std::size_t high,
426 bool form)
427 {
428 std::vector<hessenberg_panel<T>> panels;
429 for (std::size_t k {low}; k + 2 < high;) {
430 const std::size_t b {std::min(hessenberg_block, high - 2 - k)};
431 const std::size_t m {high - k - 1}; // the rows k + 1 … high − 1
432 buffer<T> v(m * b, T {});
433 buffer<T> t(b * b, T {});
434 buffer<T> y(high * b, T {}); // Y = A V T, row-major, rows 0 … high − 1
435 buffer<T> column(m, T {});
436 buffer<T> w(b, T {});
437 for (std::size_t i {0}; i < b; ++i) {
438 const std::size_t j {k + i};
439 for (std::size_t r {0}; r < m; ++r) {
440 column[r] = a[((k + 1 + r) * n) + j];
441 }
442 if (i > 0) {
443 for (std::size_t r {0}; r < m; ++r) { // A Q: the column less Y Vᴴ e_j
444 T sum {};
445 for (std::size_t p {0}; p < i; ++p) {
446 sum += times(y[((k + 1 + r) * b) + p], conj_of(v[((i - 1) * b) + p]));
447 }
448 column[r] -= sum;
449 }
450 for (std::size_t p {0}; p < i; ++p) { // then Qᴴ from the left: w = Tᴴ Vᴴ column, column −= V w
451 T sum {};
452 for (std::size_t r {p}; r < m; ++r) {
453 sum += times(conj_of(v[(r * b) + p]), column[r]);
454 }
455 w[p] = sum;
456 }
457 for (std::size_t p {i}; p-- > 0;) {
458 T sum {};
459 for (std::size_t q {0}; q <= p; ++q) {
460 sum += times(conj_of(t[(q * b) + p]), w[q]);
461 }
462 w[p] = sum;
463 }
464 for (std::size_t r {0}; r < m; ++r) {
465 T sum {};
466 for (std::size_t p {0}; p < std::min(i, r + 1); ++p) {
467 sum += times(v[(r * b) + p], w[p]);
468 }
469 column[r] -= sum;
470 }
471 }
472 T alpha {column[i]};
473 const T tau {reflector_tau(alpha, column.data() + i + 1, m - i - 1)};
474 v[(i * b) + i] = T {1};
475 for (std::size_t r {i + 1}; r < m; ++r) {
476 v[(r * b) + i] = column[r];
477 }
478 for (std::size_t r {0}; r < m; ++r) {
479 a[((k + 1 + r) * n) + j] = r < i ? column[r] : (r == i ? alpha : T {});
480 }
481 // Y's column: τ (A v − Y Vᴴ v), A's columns past j as the panel found them, each row against v by dot_product
482 const auto against_v {operand_of(v.data() + (i * b) + i, b, m - i)};
483 parallel_for(m, high - j, [&](std::size_t r) {
484 y[((k + 1 + r) * b) + i] = against_v.dot(a.data() + ((k + 1 + r) * n) + j + 1);
485 });
486 for (std::size_t p {0}; p < i; ++p) {
487 T sum {};
488 for (std::size_t r {i}; r < m; ++r) {
489 sum += times(conj_of(v[(r * b) + p]), v[(r * b) + i]);
490 }
491 w[p] = sum;
492 }
493 for (std::size_t r {0}; r < m; ++r) {
494 T sum {};
495 for (std::size_t p {0}; p < i; ++p) {
496 sum += times(y[((k + 1 + r) * b) + p], w[p]);
497 }
498 y[((k + 1 + r) * b) + i] = tau * (y[((k + 1 + r) * b) + i] - sum);
499 }
500 for (std::size_t p {0}; p < i; ++p) { // T's column: −τ T w
501 T sum {};
502 for (std::size_t q {p}; q < i; ++q) {
503 sum += times(t[(p * b) + q], w[q]);
504 }
505 t[(p * b) + i] = -tau * sum;
506 }
507 t[(i * b) + i] = tau;
508 }
509 buffer<T> top((k + 1) * b, T {}); // Y's rows 0 … k: A(0 … k, k + 1 …) V T
510 gemm(k + 1, b, m, a.data() + k + 1, n, v.data(), b, top.data(), b);
511 for (std::size_t r {0}; r <= k; ++r) {
512 for (std::size_t p {b}; p-- > 0;) {
513 T sum {};
514 for (std::size_t q {0}; q <= p; ++q) {
515 sum += times(top[(r * b) + q], t[(q * b) + p]);
516 }
517 y[(r * b) + p] = sum;
518 }
519 }
520 for (std::size_t r {0}; r <= k; ++r) { // the panel's columns above it: A − Y Vᴴ
521 for (std::size_t i {1}; i < b; ++i) {
522 T sum {};
523 for (std::size_t p {0}; p < i; ++p) {
524 sum += times(y[(r * b) + p], conj_of(v[((i - 1) * b) + p]));
525 }
526 a[(r * n) + k + i] -= sum;
527 }
528 }
529 const std::size_t rest {high - k - b}; // the columns k + b … high − 1, from the right
530 buffer<T> minus_y(high * b);
531 for (std::size_t e {0}; e < high * b; ++e) {
532 minus_y[e] = -y[e];
533 }
534 buffer<T> v_adjoint(b * m);
535 for (std::size_t r {0}; r < m; ++r) {
536 for (std::size_t p {0}; p < b; ++p) {
537 v_adjoint[(p * m) + r] = conj_of(v[(r * b) + p]);
538 }
539 }
540 gemm(high, rest, b, minus_y.data(), b, v_adjoint.data() + b - 1, m, a.data() + k + b, n);
541 const std::size_t columns {n - k - b}; // then rows k + 1 … high − 1, columns k + b …, from the left
542 buffer<T> product(b * columns, T {});
543 gemm(b, columns, m, v_adjoint.data(), m, a.data() + ((k + 1) * n) + k + b, n, product.data(), columns);
544 buffer<T> scaled(b * columns, T {});
545 for (std::size_t p {0}; p < b; ++p) {
546 for (std::size_t q {0}; q <= p; ++q) {
547 const T factor {-conj_of(t[(q * b) + p])};
548 for (std::size_t c {0}; c < columns; ++c) {
549 scaled[(p * columns) + c] += times(factor, product[(q * columns) + c]);
550 }
551 }
552 }
553 gemm(m, columns, b, v.data(), b, scaled.data(), columns, a.data() + ((k + 1) * n) + k + b, n);
554 if (form) {
555 panels.push_back({k, b, std::move(v), std::move(t)});
556 }
557 k += b;
558 }
559 if (!form) {
560 return {};
561 }
562 buffer<T> q(n * n, T {});
563 for (std::size_t i {0}; i < n; ++i) {
564 q[(i * n) + i] = T {1};
565 }
566 for (std::size_t s {panels.size()}; s-- > 0;) { // Q = Q₁ … Q_p, backward: Q ← (I − V T Vᴴ) Q on rows first + 1 …
567 const hessenberg_panel<T>& panel {panels[s]};
568 const std::size_t m {high - panel.first - 1};
569 const std::size_t b {panel.width};
570 T* const block {q.data() + ((panel.first + 1) * n) + panel.first + 1};
571 buffer<T> v_adjoint(b * m);
572 for (std::size_t r {0}; r < m; ++r) {
573 for (std::size_t p {0}; p < b; ++p) {
574 v_adjoint[(p * m) + r] = conj_of(panel.v[(r * b) + p]);
575 }
576 }
577 buffer<T> product(b * m, T {});
578 gemm(b, m, m, v_adjoint.data(), m, block, n, product.data(), m);
579 buffer<T> scaled(b * m, T {});
580 for (std::size_t p {0}; p < b; ++p) {
581 for (std::size_t r {p}; r < b; ++r) {
582 const T factor {-panel.t[(p * b) + r]};
583 for (std::size_t c {0}; c < m; ++c) {
584 scaled[(p * m) + c] += times(factor, product[(r * m) + c]);
585 }
586 }
587 }
588 gemm(m, m, b, panel.v.data(), b, scaled.data(), m, block, n);
589 }
590 buffer<T> qt(n * n, T {});
591 for (std::size_t i {0}; i < n; ++i) {
592 for (std::size_t j {0}; j < n; ++j) {
593 qt[(j * n) + i] = q[(i * n) + j];
594 }
595 }
596 return qt;
597 }
598
600 template <typename T>
602 std::size_t n,
603 std::size_t low,
604 std::size_t high,
605 bool form)
606 {
607 if (high - low >= hessenberg_crossover) {
608 return hessenberg_blocked(a, n, low, high, form);
609 }
610 return hessenberg_unblocked(a, n, low, high, form);
611 }
612
618 [[nodiscard]] inline double small_reflector(double (&v)[3],
619 std::size_t count)
620 {
621 const double rest {count == 3 ? std::hypot(v[1], v[2]) : std::abs(v[1])};
622 if (rest == 0.0) {
623 return 0.0;
624 }
625 const double beta {-std::copysign(std::hypot(v[0], rest), v[0])};
626 const double tau {(beta - v[0]) / beta};
627 const double scale {1.0 / (v[0] - beta)};
628 for (std::size_t i {1}; i < count; ++i) {
629 v[i] *= scale;
630 }
631 v[0] = beta;
632 return tau;
633 }
634
640 [[nodiscard]] inline std::complex<double> complex_reflector(std::complex<double> (&v)[2])
641 {
642 if (modulus_1(v[1]) == 0.0 && v[0].imag() == 0.0) {
643 return {};
644 }
645 const double beta {-std::copysign(std::hypot(std::abs(v[0]), std::abs(v[1])), v[0].real())};
646 const std::complex<double> tau {(beta - v[0].real()) / beta, -v[0].imag() / beta};
647 v[1] = v[1] / (v[0] - beta);
648 v[0] = beta;
649 return tau;
650 }
651
654 {
655 double cs {1.0};
656 double sn {0.0};
657 double re1 {0.0};
658 double im1 {0.0};
659 double re2 {0.0};
660 double im2 {0.0};
661 };
662
669 double& b,
670 double& c,
671 double& d)
672 {
674 if (b == 0.0 && c != 0.0) { // lower triangular: exchange the rows and the columns
675 out.cs = 0.0;
676 out.sn = 1.0;
677 std::swap(a, d);
678 b = -c;
679 c = 0.0;
680 }
681 else if (c != 0.0 && (a - d != 0.0 || std::signbit(b) == std::signbit(c))) { // not yet triangular nor standard
682 const double temp {a - d};
683 double p {0.5 * temp};
684 const double bcmax {std::max(std::abs(b), std::abs(c))};
685 const double bcmis {std::min(std::abs(b), std::abs(c)) * std::copysign(1.0, b) * std::copysign(1.0, c)};
686 const double scale {std::max(std::abs(p), bcmax)};
687 double z {((p / scale) * p) + ((bcmax / scale) * bcmis)};
688 if (z >= 4.0 * schur_ulp) { // real eigenvalues: the triangular form
689 z = p + std::copysign(std::sqrt(scale) * std::sqrt(z), p);
690 a = d + z;
691 d -= (bcmax / z) * bcmis;
692 const double tau {std::hypot(c, z)};
693 out.cs = z / tau;
694 out.sn = c / tau;
695 b -= c;
696 c = 0.0;
697 }
698 else { // complex or nearly equal real eigenvalues: equal diagonal entries first
699 const double sigma {b + c};
700 const double tau {std::hypot(sigma, temp)};
701 out.cs = std::sqrt(0.5 * (1.0 + (std::abs(sigma) / tau)));
702 out.sn = -(0.5 * (temp / tau) / out.cs) * std::copysign(1.0, sigma);
703 const double aa {(a * out.cs) + (b * out.sn)};
704 const double bb {(-a * out.sn) + (b * out.cs)};
705 const double cc {(c * out.cs) + (d * out.sn)};
706 const double dd {(-c * out.sn) + (d * out.cs)};
707 a = (aa * out.cs) + (cc * out.sn);
708 b = (bb * out.cs) + (dd * out.sn);
709 c = (-aa * out.sn) + (cc * out.cs);
710 d = (-bb * out.sn) + (dd * out.cs);
711 p = 0.5 * (a + d);
712 a = p;
713 d = p;
714 if (c != 0.0 && b != 0.0 && std::signbit(b) == std::signbit(c)) { // real after all: triangular
715 const double sab {std::sqrt(std::abs(b))};
716 const double sac {std::sqrt(std::abs(c))};
717 const double root {std::copysign(sab * sac, c)};
718 const double inverse {1.0 / std::sqrt(std::abs(b + c))};
719 a = p + root;
720 d = p - root;
721 b -= c;
722 c = 0.0;
723 const double cs1 {sab * inverse};
724 const double sn1 {sac * inverse};
725 const double cs {(out.cs * cs1) - (out.sn * sn1)};
726 out.sn = (out.cs * sn1) + (out.sn * cs1);
727 out.cs = cs;
728 }
729 else if (c != 0.0 && b == 0.0) {
730 b = -c;
731 c = 0.0;
732 const double cs {out.cs};
733 out.cs = -out.sn;
734 out.sn = cs;
735 }
736 }
737 }
738 out.re1 = a;
739 out.re2 = d;
740 if (c != 0.0) {
741 out.im1 = std::sqrt(std::abs(b)) * std::sqrt(std::abs(c));
742 out.im2 = -out.im1;
743 }
744 return out;
745 }
746
748 template <typename T>
749 void rotate(T* x,
750 T* y,
751 std::size_t count,
752 std::size_t stride,
753 double cs,
754 double sn)
755 {
756 for (std::size_t i {0}; i < count; ++i) {
757 const T xi {x[i * stride]};
758 const T yi {y[i * stride]};
759 x[i * stride] = (cs * xi) + (sn * yi);
760 y[i * stride] = (cs * yi) - (sn * xi);
761 }
762 }
763
769 template <typename T>
771 T* r2,
772 T* r3,
773 std::size_t from,
774 std::size_t to,
775 const T& tau,
776 const T& v2,
777 const T& v3,
778 bool adjoint)
779 {
780 const T f {adjoint ? conj_of(tau) : tau};
781 const T a2 {adjoint ? conj_of(v2) : v2};
782 const T a3 {adjoint ? conj_of(v3) : v3};
783 const T b2 {adjoint ? v2 : conj_of(v2)};
784 const T b3 {adjoint ? v3 : conj_of(v3)};
785 for (std::size_t j {from}; j < to; ++j) {
786 const T sum {times(f, r1[j] + times(a2, r2[j]) + times(a3, r3[j]))};
787 r1[j] -= sum;
788 r2[j] -= times(sum, b2);
789 r3[j] -= times(sum, b3);
790 }
791 }
792
795 template <typename T>
797 T* r2,
798 std::size_t from,
799 std::size_t to,
800 const T& tau,
801 const T& v2,
802 bool adjoint)
803 {
804 const T f {adjoint ? conj_of(tau) : tau};
805 const T a2 {adjoint ? conj_of(v2) : v2};
806 const T b2 {adjoint ? v2 : conj_of(v2)};
807 for (std::size_t j {from}; j < to; ++j) {
808 const T sum {times(f, r1[j] + times(a2, r2[j]))};
809 r1[j] -= sum;
810 r2[j] -= times(sum, b2);
811 }
812 }
813
819 template <typename T>
821 std::size_t rows,
822 std::size_t ld,
823 const T& tau,
824 const T& v2,
825 const T& v3)
826 {
827 const T w2 {conj_of(v2)};
828 const T w3 {conj_of(v3)};
829 for (std::size_t r {0}; r < rows; ++r) {
830 T* const x {first + (r * ld)};
831 const T sum {times(tau, x[0] + times(v2, x[1]) + times(v3, x[2]))};
832 x[0] -= sum;
833 x[1] -= times(sum, w2);
834 x[2] -= times(sum, w3);
835 }
836 }
837
839 template <typename T>
841 std::size_t rows,
842 std::size_t ld,
843 const T& tau,
844 const T& v2)
845 {
846 const T w2 {conj_of(v2)};
847 for (std::size_t r {0}; r < rows; ++r) {
848 T* const x {first + (r * ld)};
849 const T sum {times(tau, x[0] + times(v2, x[1]))};
850 x[0] -= sum;
851 x[1] -= times(sum, w2);
852 }
853 }
854
856 template <typename T>
858 {
859 buffer<T>* h {nullptr};
860 std::size_t n {0};
861 std::size_t low {0};
862 std::size_t high {0};
863 bool full {false};
864 buffer<T>* zt {nullptr};
865 };
866
878 inline void real_schur(const schur_target<double>& t,
879 buffer<double>& re,
880 buffer<double>& im,
881 std::size_t steps)
882 {
883 buffer<double>& h {*t.h};
884 const std::size_t n {t.n};
885 const auto at {[&h, n](std::size_t i, std::size_t j) -> double& { return h[(i * n) + j]; }};
886 const double smallest {std::numeric_limits<double>::min() * (static_cast<double>(t.high - t.low) / schur_ulp)};
887 std::size_t since_deflation {0};
888 for (std::size_t end {t.high}; end > t.low;) {
889 const std::size_t i {end - 1};
890 std::size_t l {t.low};
891 bool converged {false};
892 for (std::size_t its {0}; its <= steps; ++its) {
893 std::size_t k {i};
894 for (; k > l; --k) {
895 const double sub {std::abs(at(k, k - 1))};
896 if (sub <= smallest) {
897 break;
898 }
899 double test {std::abs(at(k - 1, k - 1)) + std::abs(at(k, k))};
900 if (test == 0.0) {
901 if (k >= t.low + 2) {
902 test += std::abs(at(k - 1, k - 2));
903 }
904 if (k + 1 < t.high) {
905 test += std::abs(at(k + 1, k));
906 }
907 }
908 if (sub <= schur_ulp * test) {
909 const double ab {std::max(sub, std::abs(at(k - 1, k)))};
910 const double ba {std::min(sub, std::abs(at(k - 1, k)))};
911 const double aa {std::max(std::abs(at(k, k)), std::abs(at(k - 1, k - 1) - at(k, k)))};
912 const double bb {std::min(std::abs(at(k, k)), std::abs(at(k - 1, k - 1) - at(k, k)))};
913 const double s {aa + ab};
914 if (ba * (ab / s) <= std::max(smallest, schur_ulp * (bb * (aa / s)))) {
915 break;
916 }
917 }
918 }
919 l = k;
920 if (l > t.low) {
921 at(l, l - 1) = 0.0;
922 }
923 if (l + 1 >= i) {
924 converged = true;
925 break;
926 }
928 const std::size_t first {t.full ? 0 : l}; // the columns the right reflections update start here
929 const std::size_t last {t.full ? n : i + 1}; // the rows the left reflections update end before here
930 double h11 {};
931 double h12 {};
932 double h21 {};
933 double h22 {};
934 if (since_deflation % 20 == 0) {
935 const double s {std::abs(at(i, i - 1)) + std::abs(at(i - 1, i - 2))};
936 h11 = (0.75 * s) + at(i, i);
937 h12 = -0.4375 * s;
938 h21 = s;
939 h22 = h11;
940 }
941 else if (since_deflation % 10 == 0) {
942 const double s {std::abs(at(l + 1, l)) + std::abs(at(l + 2, l + 1))};
943 h11 = (0.75 * s) + at(l, l);
944 h12 = -0.4375 * s;
945 h21 = s;
946 h22 = h11;
947 }
948 else {
949 h11 = at(i - 1, i - 1);
950 h21 = at(i, i - 1);
951 h12 = at(i - 1, i);
952 h22 = at(i, i);
953 }
954 const double s {std::abs(h11) + std::abs(h12) + std::abs(h21) + std::abs(h22)}; // > 0: h21 is not negligible
955 h11 /= s;
956 h21 /= s;
957 h12 /= s;
958 h22 /= s;
959 const double trace {(h11 + h22) / 2.0};
960 const double det {((h11 - trace) * (h22 - trace)) - (h12 * h21)};
961 const double root {std::sqrt(std::abs(det))};
962 double rt1r {trace * s};
963 double rt2r {rt1r};
964 double rt1i {root * s};
965 double rt2i {-rt1i};
966 if (det < 0.0) { // real shifts: the one nearer h22, twice
967 rt1r = trace + root;
968 rt2r = trace - root;
969 rt1r = (std::abs(rt1r - h22) <= std::abs(rt2r - h22) ? rt1r : rt2r) * s;
970 rt2r = rt1r;
971 rt1i = 0.0;
972 rt2i = 0.0;
973 }
974 std::size_t m {i - 2};
975 double v[3] {};
976 for (;; --m) { // where two consecutive small subdiagonals let the sweep start
977 const double h21s {at(m + 1, m) / (std::abs(at(m, m) - rt2r) + std::abs(rt2i) + std::abs(at(m + 1, m)))};
978 const double scale {std::abs(at(m, m) - rt2r) + std::abs(rt2i) + std::abs(at(m + 1, m))};
979 v[0] = (h21s * at(m, m + 1)) + ((at(m, m) - rt1r) * ((at(m, m) - rt2r) / scale)) - (rt1i * (rt2i / scale));
980 v[1] = h21s * (at(m, m) + at(m + 1, m + 1) - rt1r - rt2r);
981 v[2] = h21s * at(m + 2, m + 1);
982 const double sum {std::abs(v[0]) + std::abs(v[1]) + std::abs(v[2])};
983 v[0] /= sum;
984 v[1] /= sum;
985 v[2] /= sum;
986 if (m == l) {
987 break;
988 }
989 const double above {std::abs(at(m, m - 1)) * (std::abs(v[1]) + std::abs(v[2]))};
990 const double here {std::abs(v[0]) * (std::abs(at(m - 1, m - 1)) + std::abs(at(m, m)) + std::abs(at(m + 1, m + 1)))};
991 if (above <= schur_ulp * here) {
992 break;
993 }
994 }
995 for (std::size_t k2 {m}; k2 < i; ++k2) { // the bulge chased down the diagonal
996 const std::size_t count {std::min<std::size_t>(3, i - k2 + 1)};
997 if (k2 > m) {
998 v[0] = at(k2, k2 - 1);
999 v[1] = at(k2 + 1, k2 - 1);
1000 v[2] = count == 3 ? at(k2 + 2, k2 - 1) : 0.0;
1001 }
1002 const double t1 {small_reflector(v, count)};
1003 if (k2 > m) {
1004 at(k2, k2 - 1) = v[0];
1005 at(k2 + 1, k2 - 1) = 0.0;
1006 if (count == 3) {
1007 at(k2 + 2, k2 - 1) = 0.0;
1008 }
1009 }
1010 else if (m > l) {
1011 at(k2, k2 - 1) *= 1.0 - t1;
1012 }
1013 const double v2 {v[1]};
1014 const double v3 {count == 3 ? v[2] : 0.0};
1015 const double t2 {t1 * v2};
1016 const double t3 {t1 * v3};
1017 const std::size_t third {count == 3 ? std::size_t {2} : std::size_t {1}}; // two rows: the third is the second, t3 = 0 leaves it
1018 double* a1 {&at(k2, 0)};
1019 double* a2 {a1 + n};
1020 double* a3 {a1 + (third * n)};
1021 for (std::size_t j {k2}; j < last; ++j) {
1022 const double sum {a1[j] + (v2 * a2[j]) + (v3 * a3[j])};
1023 a1[j] -= sum * t1;
1024 a2[j] -= sum * t2;
1025 a3[j] -= sum * t3;
1026 }
1027 for (std::size_t j {first}; j <= std::min(k2 + 3, i); ++j) {
1028 double* row {&at(j, k2)};
1029 const double sum {row[0] + (v2 * row[1]) + (v3 * row[third])};
1030 row[0] -= sum * t1;
1031 row[1] -= sum * t2;
1032 row[third] -= sum * t3;
1033 }
1034 if (t.zt != nullptr) { // Z's columns k2 …, rows of Zᵀ
1035 buffer<double>& zt {*t.zt};
1036 double* r1 {zt.data() + (k2 * n)};
1037 double* r2 {r1 + n};
1038 double* r3 {r1 + (third * n)};
1039 for (std::size_t j {t.low}; j < t.high; ++j) {
1040 const double sum {r1[j] + (v2 * r2[j]) + (v3 * r3[j])};
1041 r1[j] -= sum * t1;
1042 r2[j] -= sum * t2;
1043 r3[j] -= sum * t3; // r3 is r2 and t3 0 for two rows
1044 }
1045 }
1046 }
1047 }
1048 if (!converged) {
1049 throw convergence_error("eigendecomposition: the QR iterations did not converge");
1050 }
1051 if (l == i) {
1052 re[i] = at(i, i);
1053 im[i] = 0.0;
1054 }
1055 else { // a 2 × 2 block: its standard form, the rotation carried to the rest of the Schur form and Z
1056 const standard_block block {standardize_block(at(i - 1, i - 1), at(i - 1, i), at(i, i - 1), at(i, i))};
1057 re[i - 1] = block.re1;
1058 im[i - 1] = block.im1;
1059 re[i] = block.re2;
1060 im[i] = block.im2;
1061 if (t.full) {
1062 rotate(&at(i - 1, i + 1 < n ? i + 1 : i), &at(i, i + 1 < n ? i + 1 : i), n - i - 1, 1, block.cs, block.sn);
1063 rotate(&at(0, i - 1), &at(0, i), i - 1, n, block.cs, block.sn);
1064 }
1065 if (t.zt != nullptr) {
1066 buffer<double>& zt {*t.zt};
1067 rotate(zt.data() + ((i - 1) * n) + t.low, zt.data() + (i * n) + t.low, t.high - t.low, 1, block.cs, block.sn);
1068 }
1069 }
1070 since_deflation = 0;
1071 end = l;
1072 }
1073 }
1074
1084 inline void complex_schur(const schur_target<std::complex<double>>& t,
1085 buffer<std::complex<double>>& values,
1086 std::size_t steps)
1087 {
1088 using C = std::complex<double>;
1089 buffer<C>& h {*t.h};
1090 const std::size_t n {t.n};
1091 const auto at {[&h, n](std::size_t i, std::size_t j) -> C& { return h[(i * n) + j]; }};
1092 const double smallest {std::numeric_limits<double>::min() * (static_cast<double>(t.high - t.low) / schur_ulp)};
1093 std::size_t since_deflation {0};
1094 for (std::size_t end {t.high}; end > t.low;) {
1095 const std::size_t i {end - 1};
1096 std::size_t l {t.low};
1097 bool converged {false};
1098 for (std::size_t its {0}; its <= steps; ++its) {
1099 std::size_t k {i};
1100 for (; k > l; --k) {
1101 const double sub {modulus_1(at(k, k - 1))};
1102 if (sub <= smallest) {
1103 break;
1104 }
1105 double test {modulus_1(at(k - 1, k - 1)) + modulus_1(at(k, k))};
1106 if (test == 0.0) {
1107 if (k >= t.low + 2) {
1108 test += modulus_1(at(k - 1, k - 2));
1109 }
1110 if (k + 1 < t.high) {
1111 test += modulus_1(at(k + 1, k));
1112 }
1113 }
1114 if (sub <= schur_ulp * test) {
1115 const double ab {std::max(sub, modulus_1(at(k - 1, k)))};
1116 const double ba {std::min(sub, modulus_1(at(k - 1, k)))};
1117 const double aa {std::max(modulus_1(at(k, k)), modulus_1(at(k - 1, k - 1) - at(k, k)))};
1118 const double bb {std::min(modulus_1(at(k, k)), modulus_1(at(k - 1, k - 1) - at(k, k)))};
1119 const double s {aa + ab};
1120 if (ba * (ab / s) <= std::max(smallest, schur_ulp * (bb * (aa / s)))) {
1121 break;
1122 }
1123 }
1124 }
1125 l = k;
1126 if (l > t.low) {
1127 at(l, l - 1) = C {};
1128 }
1129 if (l >= i) {
1130 converged = true;
1131 break;
1132 }
1134 const std::size_t first {t.full ? 0 : l};
1135 const std::size_t last {t.full ? n : i + 1};
1136 C shift {};
1137 if (since_deflation % 20 == 0) {
1138 shift = (0.75 * modulus_1(at(i, i - 1))) + at(i, i);
1139 }
1140 else if (since_deflation % 10 == 0) {
1141 shift = (0.75 * modulus_1(at(l + 1, l))) + at(l, l);
1142 }
1143 else { // Wilkinson's: the eigenvalue of the trailing 2 × 2 nearer its last diagonal entry
1144 shift = at(i, i);
1145 const C u {std::sqrt(at(i - 1, i)) * std::sqrt(at(i, i - 1))};
1146 if (modulus_1(u) != 0.0) {
1147 const C x {0.5 * (at(i - 1, i - 1) - shift)};
1148 const double sx {modulus_1(x)};
1149 const double s {std::max(modulus_1(u), sx)};
1150 C y {s * std::sqrt(((x / s) * (x / s)) + ((u / s) * (u / s)))};
1151 if (sx > 0.0 && ((x / sx).real() * y.real()) + ((x / sx).imag() * y.imag()) < 0.0) {
1152 y = -y;
1153 }
1154 shift -= u * (u / (x + y));
1155 }
1156 }
1157 std::size_t m {i - 1};
1158 C v[2] {};
1159 for (;; --m) {
1160 const C h11 {at(m, m)};
1161 const C h22 {at(m + 1, m + 1)};
1162 const C h21 {at(m + 1, m)};
1163 const double s {modulus_1(h11 - shift) + modulus_1(h21)};
1164 v[0] = (h11 - shift) / s;
1165 v[1] = h21 / s;
1166 if (m == l) {
1167 break;
1168 }
1169 if (modulus_1(at(m, m - 1)) * modulus_1(v[1]) <= schur_ulp * (modulus_1(v[0]) * (modulus_1(h11) + modulus_1(h22)))) {
1170 break;
1171 }
1172 }
1173 for (std::size_t k2 {m}; k2 < i; ++k2) {
1174 if (k2 > m) {
1175 v[0] = at(k2, k2 - 1);
1176 v[1] = at(k2 + 1, k2 - 1);
1177 }
1178 const C t1 {complex_reflector(v)};
1179 if (k2 > m) {
1180 at(k2, k2 - 1) = v[0];
1181 at(k2 + 1, k2 - 1) = C {};
1182 }
1183 else if (m > l) {
1184 at(k2, k2 - 1) *= 1.0 - std::conj(t1);
1185 }
1186 const C v2 {v[1]};
1187 reflect_two_rows(&at(k2, 0), &at(k2 + 1, 0), k2, last, t1, v2, true);
1188 reflect_two_columns(&at(first, k2), std::min(k2 + 2, i) - first + 1, n, t1, v2);
1189 if (t.zt != nullptr) {
1190 C* const r1 {t.zt->data() + (k2 * n)};
1191 reflect_two_rows(r1, r1 + n, t.low, t.high, t1, v2, false);
1192 }
1193 }
1194 }
1195 if (!converged) {
1196 throw convergence_error("eigendecomposition: the QR iterations did not converge");
1197 }
1198 values[i] = at(i, i);
1199 since_deflation = 0;
1200 end = l;
1201 }
1202 }
1203
1206 using lapack_index = std::ptrdiff_t;
1207
1209 template <typename T>
1211 {
1213 one_based(T* first,
1215 : data(first), ld(distance)
1216 {}
1217
1220
1223 lapack_index j) const
1224 {
1225 return data[((i - 1) * ld) + (j - 1)];
1226 }
1227 };
1228
1230 [[nodiscard]] inline std::size_t count_of(lapack_index count)
1231 {
1232 return static_cast<std::size_t>(count);
1233 }
1234
1237 inline constexpr lapack_index multishift_crossover {75};
1238
1242 [[nodiscard]] constexpr lapack_index multishift_from(double /*real*/)
1243 {
1244 return 200;
1245 }
1246
1249 [[nodiscard]] constexpr lapack_index multishift_from(const std::complex<double>& /*complex*/)
1250 {
1251 return 100;
1252 }
1253
1255 inline constexpr lapack_index window_growth {5};
1256
1258 inline constexpr lapack_index exceptional_after {6};
1259
1261 inline constexpr lapack_index nibble {14};
1262
1264 template <typename T>
1266 {
1267 double c {1.0};
1268 T s {};
1269 T r {};
1270 };
1271
1274 double g)
1275 {
1276 if (g == 0.0) {
1277 return {1.0, 0.0, f};
1278 }
1279 if (f == 0.0) {
1280 return {0.0, std::copysign(1.0, g), std::abs(g)};
1281 }
1282 const double r {std::copysign(std::hypot(f, g), f)};
1283 return {std::abs(f) / std::abs(r), g / r, r};
1284 }
1285
1287 [[nodiscard]] inline plane_rotation<std::complex<double>> givens(const std::complex<double>& f,
1288 const std::complex<double>& g)
1289 {
1290 using C = std::complex<double>;
1291 if (g == C {}) {
1292 return {1.0, C {}, f};
1293 }
1294 if (f == C {}) {
1295 return {0.0, std::conj(g) / std::abs(g), C {std::abs(g)}};
1296 }
1297 const double size {std::abs(f)};
1298 const double d {std::hypot(size, std::abs(g))};
1299 const C phase {f / size};
1300 return {size / d, phase * std::conj(g) / d, phase * d};
1301 }
1302
1304 template <typename T>
1306 T* y,
1307 std::size_t count,
1308 std::size_t stride,
1309 double c,
1310 const T& s)
1311 {
1312 for (std::size_t i {0}; i < count; ++i) {
1313 const T xi {x[i * stride]};
1314 const T yi {y[i * stride]};
1315 x[i * stride] = (c * xi) + times(s, yi);
1316 y[i * stride] = (c * yi) - times(conj_of(s), xi);
1317 }
1318 }
1319
1325 template <typename T>
1328 lapack_index n,
1329 lapack_index k)
1330 {
1331 const T t11 {t(k, k)};
1332 const T t22 {t(k + 1, k + 1)};
1333 const plane_rotation<T> g {givens(t(k, k + 1), t22 - t11)};
1334 if (k + 2 <= n) {
1335 rotate_pair(&t(k, k + 2), &t(k + 1, k + 2), count_of(n - k - 1), 1, g.c, g.s);
1336 }
1337 rotate_pair(&t(1, k), &t(1, k + 1), count_of(k - 1), count_of(t.ld), g.c, conj_of(g.s));
1338 t(k, k) = t22;
1339 t(k + 1, k + 1) = t11;
1340 rotate_pair(&qt(k, 1), &qt(k + 1, 1), count_of(n), 1, g.c, conj_of(g.s));
1341 }
1342
1344 inline void reflect_rows_3(const std::array<double, 3>& u,
1345 double tau,
1346 double * t,
1347 std::size_t ld,
1348 std::size_t count)
1349 {
1350 for (std::size_t c {0}; c < count; ++c) {
1351 const double sum {(u[0] * t[c]) + (u[1] * t[ld + c]) + (u[2] * t[(2 * ld) + c])};
1352 t[c] -= tau * sum * u[0];
1353 t[ld + c] -= tau * sum * u[1];
1354 t[(2 * ld) + c] -= tau * sum * u[2];
1355 }
1356 }
1357
1359 inline void reflect_columns_3(const std::array<double, 3>& u,
1360 double tau,
1361 double * t,
1362 std::size_t ld,
1363 std::size_t count)
1364 {
1365 for (std::size_t r {0}; r < count; ++r) {
1366 double* const row {t + (r * ld)};
1367 const double sum {(row[0] * u[0]) + (row[1] * u[1]) + (row[2] * u[2])};
1368 row[0] -= tau * sum * u[0];
1369 row[1] -= tau * sum * u[1];
1370 row[2] -= tau * sum * u[2];
1371 }
1372 }
1373
1380 const one_based<double>& tr,
1381 const one_based<double>& b,
1382 lapack_index n1,
1384 double (&x)[4])
1385 {
1386 const std::size_t size {count_of(n1 * n2)};
1387 double k[4][4] {};
1388 double rhs[4] {};
1389 double largest {0.0};
1390 for (lapack_index i {1}; i <= n1; ++i) {
1391 for (lapack_index j {1}; j <= n2; ++j) {
1392 const std::size_t row {count_of(((i - 1) * n2) + (j - 1))};
1393 rhs[row] = b(i, j);
1394 for (lapack_index p {1}; p <= n1; ++p) {
1395 k[row][count_of(((p - 1) * n2) + (j - 1))] += tl(i, p);
1396 }
1397 for (lapack_index q {1}; q <= n2; ++q) {
1398 k[row][count_of(((i - 1) * n2) + (q - 1))] -= tr(q, j);
1399 }
1400 }
1401 }
1402 for (std::size_t r {0}; r < size; ++r) {
1403 for (std::size_t c {0}; c < size; ++c) {
1404 largest = std::max(largest, std::abs(k[r][c]));
1405 }
1406 }
1407 const double floor {std::max(schur_ulp * largest, std::numeric_limits<double>::min())};
1408 std::size_t column_of[4] {0, 1, 2, 3};
1409 for (std::size_t s {0}; s < size; ++s) {
1410 std::size_t pr {s};
1411 std::size_t pc {s};
1412 for (std::size_t r {s}; r < size; ++r) {
1413 for (std::size_t c {s}; c < size; ++c) {
1414 if (std::abs(k[r][c]) > std::abs(k[pr][pc])) {
1415 pr = r;
1416 pc = c;
1417 }
1418 }
1419 }
1420 std::swap(k[s], k[pr]);
1421 std::swap(rhs[s], rhs[pr]);
1422 for (std::size_t r {0}; r < size; ++r) {
1423 std::swap(k[r][s], k[r][pc]);
1424 }
1425 std::swap(column_of[s], column_of[pc]);
1426 if (std::abs(k[s][s]) < floor) {
1427 k[s][s] = floor;
1428 }
1429 for (std::size_t r {s + 1}; r < size; ++r) {
1430 const double factor {k[r][s] / k[s][s]};
1431 for (std::size_t c {s}; c < size; ++c) {
1432 k[r][c] -= factor * k[s][c];
1433 }
1434 rhs[r] -= factor * rhs[s];
1435 }
1436 }
1437 double y[4] {};
1438 for (std::size_t s {size}; s-- > 0;) {
1439 double sum {rhs[s]};
1440 for (std::size_t c {s + 1}; c < size; ++c) {
1441 sum -= k[s][c] * y[c];
1442 }
1443 y[s] = sum / k[s][s];
1444 }
1445 for (std::size_t s {0}; s < size; ++s) {
1446 x[column_of[s]] = y[s];
1447 }
1448 }
1449
1454 lapack_index n,
1455 lapack_index j)
1456 {
1457 const standard_block block {standardize_block(t(j, j), t(j, j + 1), t(j + 1, j), t(j + 1, j + 1))};
1458 if (j + 2 <= n) {
1459 rotate(&t(j, j + 2), &t(j + 1, j + 2), count_of(n - j - 1), 1, block.cs, block.sn);
1460 }
1461 rotate(&t(1, j), &t(1, j + 1), count_of(j - 1), count_of(t.ld), block.cs, block.sn);
1462 rotate(&qt(j, 1), &qt(j + 1, 1), count_of(n), 1, block.cs, block.sn);
1463 }
1464
1472 lapack_index n,
1474 lapack_index n1,
1476 {
1477 if (n1 == 1 && n2 == 1) {
1478 exchange_diagonal(t, qt, n, j1);
1479 return true;
1480 }
1481 const lapack_index j2 {j1 + 1};
1482 const lapack_index j3 {j1 + 2};
1483 const lapack_index j4 {j1 + 3};
1484 const lapack_index nd {n1 + n2};
1485 std::array<double, 16> d {};
1486 const one_based<double> dd {d.data(), 4};
1487 double norm {0.0};
1488 for (lapack_index i {1}; i <= nd; ++i) {
1489 for (lapack_index j {1}; j <= nd; ++j) {
1490 dd(i, j) = t(j1 + i - 1, j1 + j - 1);
1491 norm = std::max(norm, std::abs(dd(i, j)));
1492 }
1493 }
1494 const double thresh {std::max(10.0 * schur_ulp * norm, std::numeric_limits<double>::min() / schur_ulp)};
1495 double x[4] {};
1496 small_sylvester(dd, one_based<double> {&dd(n1 + 1, n1 + 1), 4}, one_based<double> {&dd(1, n1 + 1), 4}, n1, n2, x);
1497 const auto at {[&x, n2](lapack_index i, lapack_index j) { return x[count_of(((i - 1) * n2) + (j - 1))]; }};
1498 const std::size_t tld {count_of(t.ld)};
1499 std::array<double, 3> u1 {};
1500 std::array<double, 3> u2 {};
1501 double tau1 {};
1502 double tau2 {};
1503 double residue {};
1504 const double t11 {t(j1, j1)};
1505 const double t33 {t(j1 + nd - 1, j1 + nd - 1)};
1506 if (n1 == 1) { // and n2 == 2: one reflection, tried on the block first
1507 u1[0] = 1.0;
1508 u1[1] = at(1, 1);
1509 u1[2] = at(1, 2);
1510 double alpha {u1[2]};
1511 tau1 = reflector_tau(alpha, u1.data(), 2);
1512 u1[2] = 1.0;
1513 reflect_rows_3(u1, tau1, d.data(), 4, 3);
1514 reflect_columns_3(u1, tau1, d.data(), 4, 3);
1515 residue = std::max({std::abs(dd(3, 1)), std::abs(dd(3, 2)), std::abs(dd(3, 3) - t11)});
1516 }
1517 else if (n2 == 1) { // n1 == 2
1518 u1[0] = -at(1, 1);
1519 u1[1] = -at(2, 1);
1520 u1[2] = 1.0;
1521 double alpha {u1[0]};
1522 tau1 = reflector_tau(alpha, u1.data() + 1, 2);
1523 u1[0] = 1.0;
1524 reflect_rows_3(u1, tau1, d.data(), 4, 3);
1525 reflect_columns_3(u1, tau1, d.data(), 4, 3);
1526 residue = std::max({std::abs(dd(2, 1)), std::abs(dd(3, 1)), std::abs(dd(1, 1) - t33)});
1527 }
1528 else { // two 2 × 2 blocks: two reflections
1529 u1[0] = -at(1, 1);
1530 u1[1] = -at(2, 1);
1531 u1[2] = 1.0;
1532 double alpha1 {u1[0]};
1533 tau1 = reflector_tau(alpha1, u1.data() + 1, 2);
1534 u1[0] = 1.0;
1535 const double temp {-tau1 * (at(1, 2) + (u1[1] * at(2, 2)))};
1536 u2[0] = (-temp * u1[1]) - at(2, 2);
1537 u2[1] = -temp * u1[2];
1538 u2[2] = 1.0;
1539 double alpha2 {u2[0]};
1540 tau2 = reflector_tau(alpha2, u2.data() + 1, 2);
1541 u2[0] = 1.0;
1542 reflect_rows_3(u1, tau1, d.data(), 4, 4);
1543 reflect_columns_3(u1, tau1, d.data(), 4, 4);
1544 reflect_rows_3(u2, tau2, &dd(2, 1), 4, 4);
1545 reflect_columns_3(u2, tau2, &dd(1, 2), 4, 4);
1546 residue = std::max({std::abs(dd(3, 1)), std::abs(dd(3, 2)), std::abs(dd(4, 1)), std::abs(dd(4, 2))});
1547 }
1548 if (residue > thresh) { // the swap would not leave the blocks apart: refused, T as it was
1549 return false;
1550 }
1551 if (n1 == 1) {
1552 reflect_rows_3(u1, tau1, &t(j1, j1), tld, count_of(n - j1 + 1));
1553 reflect_columns_3(u1, tau1, &t(1, j1), tld, count_of(j2));
1554 t(j3, j1) = 0.0;
1555 t(j3, j2) = 0.0;
1556 t(j3, j3) = t11;
1557 reflect_rows_3(u1, tau1, &qt(j1, 1), count_of(qt.ld), count_of(n));
1558 }
1559 else if (n2 == 1) {
1560 reflect_columns_3(u1, tau1, &t(1, j1), tld, count_of(j3));
1561 reflect_rows_3(u1, tau1, &t(j1, j2), tld, count_of(n - j1));
1562 t(j1, j1) = t33;
1563 t(j2, j1) = 0.0;
1564 t(j3, j1) = 0.0;
1565 reflect_rows_3(u1, tau1, &qt(j1, 1), count_of(qt.ld), count_of(n));
1566 }
1567 else {
1568 reflect_rows_3(u1, tau1, &t(j1, j1), tld, count_of(n - j1 + 1));
1569 reflect_columns_3(u1, tau1, &t(1, j1), tld, count_of(j4));
1570 reflect_rows_3(u2, tau2, &t(j2, j1), tld, count_of(n - j1 + 1));
1571 reflect_columns_3(u2, tau2, &t(1, j2), tld, count_of(j4));
1572 t(j3, j1) = 0.0;
1573 t(j3, j2) = 0.0;
1574 t(j4, j1) = 0.0;
1575 t(j4, j2) = 0.0;
1576 reflect_rows_3(u1, tau1, &qt(j1, 1), count_of(qt.ld), count_of(n));
1577 reflect_rows_3(u2, tau2, &qt(j2, 1), count_of(qt.ld), count_of(n));
1578 }
1579 if (n2 == 2) {
1580 standardize_at(t, qt, n, j1);
1581 }
1582 if (n1 == 2) {
1583 standardize_at(t, qt, n, j1 + n2);
1584 }
1585 return true;
1586 }
1587
1591 lapack_index n,
1592 lapack_index j)
1593 {
1594 return j < n && t(j + 1, j) != 0.0 ? 2 : 1;
1595 }
1596
1604 lapack_index n,
1607 {
1608 if (ifst > 1 && t(ifst, ifst - 1) != 0.0) {
1609 --ifst;
1610 }
1612 if (ilst > 1 && t(ilst, ilst - 1) != 0.0) {
1613 --ilst;
1614 }
1615 const lapack_index nbl {block_order(t, n, ilst)};
1616 if (ifst == ilst) {
1617 return true;
1618 }
1619 struct refused : std::exception // an exchange refused: the block stays where it got to
1620 {};
1621 const auto exchange {[&t, &qt, n](lapack_index j1, lapack_index n1, lapack_index n2) {
1622 if (!exchange_blocks(t, qt, n, j1, n1, n2)) {
1623 throw refused {};
1624 }
1625 }};
1627 try {
1628 if (ifst < ilst) {
1629 if (nbf == 2 && nbl == 1) {
1630 --ilst;
1631 }
1632 if (nbf == 1 && nbl == 2) {
1633 ++ilst;
1634 }
1635 while (here < ilst) {
1636 if (nbf != 3) { // the block, 1 × 1 or 2 × 2, past the next one
1637 const lapack_index next {here + nbf + 1 <= n && t(here + nbf + 1, here + nbf) != 0.0 ? 2 : 1};
1638 exchange(here, nbf, next);
1639 here += next;
1640 if (nbf == 2 && t(here + 1, here) == 0.0) {
1641 nbf = 3; // the 2 × 2 block split into two 1 × 1
1642 }
1643 }
1644 else { // two 1 × 1 blocks, each past the next block
1645 const lapack_index next {here + 3 <= n && t(here + 3, here + 2) != 0.0 ? 2 : 1};
1646 exchange(here + 1, 1, next);
1647 if (next == 2 && t(here + 2, here + 1) == 0.0) { // the next block split too: the first past both pieces
1648 exchange(here, 1, 1);
1649 exchange(here + 1, 1, 1);
1650 here += 2;
1651 }
1652 else {
1653 exchange(here, 1, next);
1654 here += next;
1655 }
1656 }
1657 }
1658 }
1659 else {
1660 while (here > ilst) {
1661 if (nbf != 3) {
1662 const lapack_index next {here >= 3 && t(here - 1, here - 2) != 0.0 ? 2 : 1};
1663 exchange(here - next, next, nbf);
1664 here -= next;
1665 if (nbf == 2 && t(here + 1, here) == 0.0) {
1666 nbf = 3;
1667 }
1668 }
1669 else {
1670 const lapack_index next {here >= 3 && t(here - 1, here - 2) != 0.0 ? 2 : 1};
1671 exchange(here - next, next, 1);
1672 if (next == 2 && t(here, here - 1) == 0.0) {
1673 exchange(here, 1, 1);
1674 exchange(here - 1, 1, 1);
1675 here -= 2;
1676 }
1677 else {
1678 exchange(here - next + 1, next, 1);
1679 here -= next;
1680 }
1681 }
1682 }
1683 }
1684 }
1685 catch (const refused&) {
1686 ilst = here;
1687 return false;
1688 }
1689 ilst = here;
1690 return true;
1691 }
1692
1695 inline bool reorder(one_based<std::complex<double>> t,
1696 one_based<std::complex<double>> qt,
1697 lapack_index n,
1700 {
1701 for (lapack_index k {ifst}; k < ilst; ++k) {
1702 exchange_diagonal(t, qt, n, k);
1703 }
1704 for (lapack_index k {ifst - 1}; k >= ilst; --k) {
1705 exchange_diagonal(t, qt, n, k);
1706 }
1707 return true;
1708 }
1709
1715 lapack_index count,
1716 const std::complex<double>& s1,
1717 const std::complex<double>& s2,
1718 double* v)
1719 {
1720 const double sr1 {s1.real()};
1721 const double si1 {s1.imag()};
1722 const double sr2 {s2.real()};
1723 const double si2 {s2.imag()};
1724 if (count == 2) {
1725 const double s {std::abs(h(1, 1) - sr2) + std::abs(si2) + std::abs(h(2, 1))};
1726 if (s == 0.0) {
1727 v[0] = 0.0;
1728 v[1] = 0.0;
1729 return;
1730 }
1731 const double h21s {h(2, 1) / s};
1732 v[0] = (h21s * h(1, 2)) + ((h(1, 1) - sr1) * ((h(1, 1) - sr2) / s)) - (si1 * (si2 / s));
1733 v[1] = h21s * (h(1, 1) + h(2, 2) - sr1 - sr2);
1734 return;
1735 }
1736 const double s {std::abs(h(1, 1) - sr2) + std::abs(si2) + std::abs(h(2, 1)) + std::abs(h(3, 1))};
1737 if (s == 0.0) {
1738 v[0] = 0.0;
1739 v[1] = 0.0;
1740 v[2] = 0.0;
1741 return;
1742 }
1743 const double h21s {h(2, 1) / s};
1744 const double h31s {h(3, 1) / s};
1745 v[0] = ((h(1, 1) - sr1) * ((h(1, 1) - sr2) / s)) - (si1 * (si2 / s)) + (h(1, 2) * h21s) + (h(1, 3) * h31s);
1746 v[1] = (h21s * (h(1, 1) + h(2, 2) - sr1 - sr2)) + (h(2, 3) * h31s);
1747 v[2] = (h31s * (h(1, 1) + h(3, 3) - sr1 - sr2)) + (h21s * h(3, 2));
1748 }
1749
1751 inline void bulge_column(one_based<std::complex<double>> h,
1752 lapack_index count,
1753 const std::complex<double>& s1,
1754 const std::complex<double>& s2,
1755 std::complex<double>* v)
1756 {
1757 using C = std::complex<double>;
1758 if (count == 2) {
1759 const double s {modulus_1(h(1, 1) - s2) + modulus_1(h(2, 1))};
1760 if (s == 0.0) {
1761 v[0] = C {};
1762 v[1] = C {};
1763 return;
1764 }
1765 const C h21s {h(2, 1) / s};
1766 v[0] = (h21s * h(1, 2)) + ((h(1, 1) - s1) * ((h(1, 1) - s2) / s));
1767 v[1] = h21s * (h(1, 1) + h(2, 2) - s1 - s2);
1768 return;
1769 }
1770 const double s {modulus_1(h(1, 1) - s2) + modulus_1(h(2, 1)) + modulus_1(h(3, 1))};
1771 if (s == 0.0) {
1772 v[0] = C {};
1773 v[1] = C {};
1774 v[2] = C {};
1775 return;
1776 }
1777 const C h21s {h(2, 1) / s};
1778 const C h31s {h(3, 1) / s};
1779 v[0] = ((h(1, 1) - s1) * ((h(1, 1) - s2) / s)) + (h21s * h(1, 2)) + (h31s * h(1, 3));
1780 v[1] = (h21s * (h(1, 1) + h(2, 2) - s1 - s2)) + (h31s * h(2, 3));
1781 v[2] = (h31s * (h(1, 1) + h(3, 3) - s1 - s2)) + (h21s * h(3, 2));
1782 }
1783
1786 inline void pair_shifts(std::complex<double>* s,
1787 lapack_index count,
1788 double /*real*/)
1789 {
1790 for (lapack_index i {0}; i + 2 < count; i += 2) {
1791 if (s[i].imag() != -s[i + 1].imag()) {
1792 const std::complex<double> first {s[i]};
1793 s[i] = s[i + 1];
1794 s[i + 1] = s[i + 2];
1795 s[i + 2] = first;
1796 }
1797 }
1798 }
1799
1801 inline void pair_shifts(std::complex<double>* /*s*/,
1802 lapack_index /*count*/,
1803 const std::complex<double>& /*complex*/)
1804 {
1805 }
1806
1808 [[nodiscard]] inline double as_scalar(const std::complex<double>& s,
1809 double /*real*/)
1810 {
1811 return s.real();
1812 }
1813
1815 [[nodiscard]] inline std::complex<double> as_scalar(const std::complex<double>& s,
1816 const std::complex<double>& /*complex*/)
1817 {
1818 return s;
1819 }
1820
1823 template <typename T>
1825 lapack_index k,
1828 double smallest)
1829 {
1830 if (h(k + 1, k) == T {}) {
1831 return;
1832 }
1833 double test {modulus_1(h(k, k)) + modulus_1(h(k + 1, k + 1))};
1834 if (test == 0.0) {
1835 for (lapack_index d {1}; d <= 3; ++d) {
1836 if (k >= ktop + d) {
1837 test += std::abs(real_part(h(k, k - d)));
1838 }
1839 if (k <= kbot - d - 1) {
1840 test += std::abs(real_part(h(k + d + 1, k + 1)));
1841 }
1842 }
1843 }
1844 if (modulus_1(h(k + 1, k)) > std::max(smallest, schur_ulp * test)) {
1845 return;
1846 }
1847 const double h12 {std::max(modulus_1(h(k + 1, k)), modulus_1(h(k, k + 1)))};
1848 const double h21 {std::min(modulus_1(h(k + 1, k)), modulus_1(h(k, k + 1)))};
1849 const double h11 {std::max(modulus_1(h(k + 1, k + 1)), modulus_1(h(k, k) - h(k + 1, k + 1)))};
1850 const double h22 {std::min(modulus_1(h(k + 1, k + 1)), modulus_1(h(k, k) - h(k + 1, k + 1)))};
1851 const double scale {h11 + h12};
1852 const double test2 {h22 * (h11 / scale)};
1853 if (test2 == 0.0 || h21 * (h12 / scale) <= std::max(smallest, schur_ulp * test2)) {
1854 h(k + 1, k) = T {};
1855 }
1856 }
1857
1860 template <typename T>
1862 std::size_t stride,
1863 const T& tau,
1864 const T& v2,
1865 const T& v3)
1866 {
1867 const T sum {times(conj_of(tau), x[0] + times(conj_of(v2), x[stride]) + times(conj_of(v3), x[2 * stride]))};
1868 x[0] -= sum;
1869 x[stride] -= times(sum, v2);
1870 x[2 * stride] -= times(sum, v3);
1871 }
1872
1881 template <typename T>
1885 std::complex<double>* s,
1886 lapack_index count,
1887 bool accumulate)
1888 {
1889 const lapack_index n {static_cast<lapack_index>(target.n)};
1890 const one_based<T> h {target.h->data(), n};
1891 pair_shifts(s, count, T {});
1892 const lapack_index ns {count - (count % 2)};
1893 const double smallest {std::numeric_limits<double>::min() * (static_cast<double>(n) / schur_ulp)};
1894 if (ktop + 2 <= kbot) {
1895 h(ktop + 2, ktop) = T {};
1896 }
1897 const lapack_index nbmps {ns / 2};
1898 const lapack_index kdu {4 * nbmps};
1900 const one_based<T> ut {u_store.data(), kdu}; // Uᵀ: the accumulation updates columns of U, contiguous in it
1901 buffer<T> v_store(count_of(3 * nbmps), T {});
1902 const auto v {[&v_store](lapack_index i, lapack_index m) -> T& { return v_store[count_of(((m - 1) * 3) + (i - 1))]; }};
1903 const lapack_index low {static_cast<lapack_index>(target.low) + 1};
1904 const lapack_index high {static_cast<lapack_index>(target.high)};
1905 for (lapack_index incol {ktop - (2 * nbmps) + 1}; incol <= kbot - 2; incol += 2 * nbmps) {
1906 const lapack_index ndcol {incol + kdu};
1907 const lapack_index jtop {accumulate ? std::max(ktop, incol) : (target.full ? 1 : ktop)};
1908 const lapack_index jbot {accumulate ? std::min(ndcol, kbot) : (target.full ? n : kbot)};
1909 const auto to_z {[&](lapack_index k, lapack_index m, std::size_t entries) { // Z ← Z H, on Zᵀ's rows
1910 if (accumulate || target.zt == nullptr) {
1911 return;
1912 }
1913 const one_based<T> zt {target.zt->data(), n};
1914 if (entries == 3) {
1915 reflect_three_rows(&zt(k + 1, 1), &zt(k + 2, 1), &zt(k + 3, 1), count_of(low - 1), count_of(high), v(1, m), v(2, m), v(3, m), false);
1916 }
1917 else {
1918 reflect_two_rows(&zt(k + 1, 1), &zt(k + 2, 1), count_of(low - 1), count_of(high), v(1, m), v(2, m), false);
1919 }
1920 }};
1921 std::fill(u_store.begin(), u_store.end(), T {});
1922 for (lapack_index d {1}; d <= kdu; ++d) {
1923 ut(d, d) = T {1};
1924 }
1925 for (lapack_index krcol {incol}; krcol <= std::min(incol + (2 * nbmps) - 1, kbot - 2); ++krcol) {
1926 const lapack_index mtop {std::max<lapack_index>(1, ((ktop - krcol) / 2) + 1)};
1927 const lapack_index mbot {std::min(nbmps, (kbot - krcol - 1) / 2)};
1928 const lapack_index m22 {mbot + 1};
1929 const bool bmp22 {mbot < nbmps && krcol + (2 * (m22 - 1)) == kbot - 2};
1930 if (bmp22) { // a 2 × 2 reflection at the bottom
1931 const lapack_index k {krcol + (2 * (m22 - 1))};
1932 if (k == ktop - 1) {
1933 std::array<T, 2> column {};
1934 bulge_column(one_based<T> {&h(k + 1, k + 1), n}, 2, s[(2 * m22) - 2], s[(2 * m22) - 1], column.data());
1935 T beta {column[0]};
1936 v(2, m22) = column[1];
1937 v(1, m22) = reflector_tau(beta, &v(2, m22), 1);
1938 }
1939 else {
1940 T beta {h(k + 1, k)};
1941 v(2, m22) = h(k + 2, k);
1942 v(1, m22) = reflector_tau(beta, &v(2, m22), 1);
1943 h(k + 1, k) = beta;
1944 h(k + 2, k) = T {};
1945 }
1946 reflect_two_columns(&h(jtop, k + 1), count_of(std::min(kbot, k + 3) - jtop + 1), count_of(n), v(1, m22), v(2, m22));
1947 reflect_two_rows(&h(k + 1, 1), &h(k + 2, 1), count_of(k), count_of(jbot), v(1, m22), v(2, m22), true);
1948 if (k >= ktop) {
1950 }
1951 const lapack_index kms {k - incol};
1952 if (accumulate) {
1953 const std::size_t from {count_of(std::max<lapack_index>(1, ktop - incol) - 1)};
1954 reflect_two_rows(&ut(kms + 1, 1), &ut(kms + 2, 1), from, count_of(kdu), v(1, m22), v(2, m22), false);
1955 }
1956 to_z(k, m22, 2);
1957 }
1958 for (lapack_index m {mbot}; m >= mtop; --m) { // the chain of 3 × 3 reflections
1959 const lapack_index k {krcol + (2 * (m - 1))};
1960 if (k == ktop - 1) {
1961 std::array<T, 3> column {};
1962 bulge_column(one_based<T> {&h(ktop, ktop), n}, 3, s[(2 * m) - 2], s[(2 * m) - 1], column.data());
1963 T alpha {column[0]};
1964 v(2, m) = column[1];
1965 v(3, m) = column[2];
1966 v(1, m) = reflector_tau(alpha, &v(2, m), 2);
1967 }
1968 else {
1969 // the row below the bulge takes the previous reflection, its first two entries being zero
1970 const T sum {v(1, m) * v(3, m) * h(k + 3, k + 2)};
1971 h(k + 3, k) = -sum;
1972 h(k + 3, k + 1) = -sum * conj_of(v(2, m));
1973 h(k + 3, k + 2) -= sum * conj_of(v(3, m));
1974 T beta {h(k + 1, k)};
1975 v(2, m) = h(k + 2, k);
1976 v(3, m) = h(k + 3, k);
1977 v(1, m) = reflector_tau(beta, &v(2, m), 2);
1978 // A bulge collapses (vigilant deflation, or destructive underflow) where the row below it holds only its
1979 // last entry: then a bulge started afresh from the shifts replaces it where its fill would be negligible
1980 // (the fill of none is infinite).
1981 double fill {std::numeric_limits<double>::infinity()};
1982 std::array<T, 3> fresh {};
1983 T fresh_tau {};
1984 T fresh_sum {};
1985 if (h(k + 3, k) == T {} && h(k + 3, k + 2) != T {}) { // the entry between is −sum v̄₂: zero with it
1986 bulge_column(one_based<T> {&h(k + 1, k + 1), n}, 3, s[(2 * m) - 2], s[(2 * m) - 1], fresh.data());
1987 T alpha {fresh[0]};
1988 fresh_tau = reflector_tau(alpha, fresh.data() + 1, 2);
1989 fresh_sum = conj_of(fresh_tau) * (h(k + 1, k) + (conj_of(fresh[1]) * h(k + 2, k)));
1990 fill = modulus_1(h(k + 2, k) - (fresh_sum * fresh[1])) + modulus_1(fresh_sum * fresh[2])
1991 - (schur_ulp * (modulus_1(h(k, k)) + modulus_1(h(k + 1, k + 1)) + modulus_1(h(k + 2, k + 2))));
1992 }
1993 if (fill <= 0.0) {
1994 h(k + 1, k) -= fresh_sum;
1995 v(1, m) = fresh_tau;
1996 v(2, m) = fresh[1];
1997 v(3, m) = fresh[2];
1998 }
1999 else {
2000 h(k + 1, k) = beta;
2001 }
2002 h(k + 2, k) = T {};
2003 h(k + 3, k) = T {};
2004 }
2005 reflect_three_columns(&h(jtop, k + 1), count_of(std::min(kbot, k + 3) - jtop + 1), count_of(n), v(1, m), v(2, m), v(3, m));
2006 reflect_from_left(&h(k + 1, k + 1), count_of(n), v(1, m), v(2, m), v(3, m));
2007 if (k >= ktop) {
2009 }
2010 }
2011 for (lapack_index m {mbot}; m >= mtop; --m) { // the rest of the left reflections, within the slab
2012 const lapack_index k {krcol + (2 * (m - 1))};
2013 reflect_three_rows(&h(k + 1, 1), &h(k + 2, 1), &h(k + 3, 1), count_of(std::max(ktop, krcol + (2 * m)) - 1), count_of(jbot), v(1, m),
2014 v(2, m), v(3, m), true);
2015 }
2016 for (lapack_index m {mbot}; m >= mtop; --m) { // accumulated in U
2017 const lapack_index k {krcol + (2 * (m - 1))};
2018 const lapack_index kms {k - incol};
2019 const lapack_index i2 {std::max({lapack_index {1}, ktop - incol, kms - (krcol - incol) + 1})};
2020 const lapack_index i4 {std::min(kdu, krcol + (2 * (mbot - 1)) - incol + 5)};
2021 if (accumulate) {
2022 reflect_three_rows(&ut(kms + 1, 1), &ut(kms + 2, 1), &ut(kms + 3, 1), count_of(i2 - 1), count_of(i4), v(1, m), v(2, m), v(3, m), false);
2023 }
2024 to_z(k, m, 3);
2025 }
2026 }
2027 if (!accumulate) {
2028 continue;
2029 }
2030 // U to the rest of the matrix: the rows right of the slab, the columns above it, and Z
2031 const lapack_index jtop_far {target.full ? 1 : ktop};
2032 const lapack_index jbot_far {target.full ? n : kbot};
2033 const lapack_index k1 {std::max<lapack_index>(1, ktop - incol)};
2034 const lapack_index nu {(kdu - std::max<lapack_index>(0, ndcol - kbot)) - k1 + 1}; // ≥ kbot − ktop + 1
2037 for (lapack_index i {1}; i <= nu; ++i) {
2038 for (lapack_index j {1}; j <= nu; ++j) {
2039 u_block[count_of(((i - 1) * nu) + (j - 1))] = ut(k1 + j - 1, k1 + i - 1);
2040 u_adjoint[count_of(((j - 1) * nu) + (i - 1))] = conj_of(ut(k1 + j - 1, k1 + i - 1));
2041 }
2042 }
2043 const lapack_index first_column {std::min(ndcol, kbot) + 1};
2044 if (first_column <= jbot_far) {
2045 const std::size_t width {count_of(jbot_far - first_column + 1)};
2046 buffer<T> product(count_of(nu) * width, T {});
2047 gemm(count_of(nu), width, count_of(nu), u_adjoint.data(), count_of(nu), &h(incol + k1, first_column), count_of(n), product.data(), width);
2048 for (lapack_index i {1}; i <= nu; ++i) {
2049 std::copy_n(product.data() + (count_of(i - 1) * width), width, &h(incol + k1 + i - 1, first_column));
2050 }
2051 }
2052 const lapack_index last_row {std::max(ktop, incol) - 1};
2053 if (jtop_far <= last_row) {
2054 const std::size_t rows {count_of(last_row - jtop_far + 1)};
2055 buffer<T> product(rows * count_of(nu), T {});
2056 gemm(rows, count_of(nu), count_of(nu), &h(jtop_far, incol + k1), count_of(n), u_block.data(), count_of(nu), product.data(), count_of(nu));
2057 for (std::size_t r {0}; r < rows; ++r) {
2058 std::copy_n(product.data() + (r * count_of(nu)), count_of(nu), &h(jtop_far + static_cast<lapack_index>(r), incol + k1));
2059 }
2060 }
2061 if (target.zt != nullptr) { // (Z U)ᵀ = Uᵀ Zᵀ, on Zᵀ's rows
2063 for (lapack_index i {1}; i <= nu; ++i) {
2064 for (lapack_index j {1}; j <= nu; ++j) {
2065 u_transpose[count_of(((j - 1) * nu) + (i - 1))] = ut(k1 + j - 1, k1 + i - 1);
2066 }
2067 }
2068 const one_based<T> zt {target.zt->data(), n};
2069 const std::size_t width {count_of(high - low + 1)};
2070 buffer<T> product(count_of(nu) * width, T {});
2071 gemm(count_of(nu), width, count_of(nu), u_transpose.data(), count_of(nu), &zt(incol + k1, low), count_of(n), product.data(), width);
2072 for (lapack_index i {1}; i <= nu; ++i) {
2073 std::copy_n(product.data() + (count_of(i - 1) * width), width, &zt(incol + k1 + i - 1, low));
2074 }
2075 }
2076 }
2077 }
2078
2079 template <typename T>
2081 std::complex<double>* w,
2082 bool recursive,
2083 lapack_index steps = 0);
2084
2086 inline void plain_schur(const schur_target<double>& t,
2087 std::complex<double>* w)
2088 {
2089 buffer<double> re(t.n, 0.0);
2090 buffer<double> im(t.n, 0.0);
2091 real_schur(t, re, im, schur_steps_per_order * std::max<std::size_t>(10, t.high - t.low));
2092 for (std::size_t i {t.low}; i < t.high; ++i) {
2093 w[i] = std::complex<double> {re[i], im[i]};
2094 }
2095 }
2096
2098 inline void plain_schur(const schur_target<std::complex<double>>& t,
2099 std::complex<double>* w)
2100 {
2101 buffer<std::complex<double>> values(t.n);
2102 complex_schur(t, values, schur_steps_per_order * std::max<std::size_t>(10, t.high - t.low));
2103 std::copy(values.begin() + static_cast<std::ptrdiff_t>(t.low), values.begin() + static_cast<std::ptrdiff_t>(t.high), w + t.low);
2104 }
2105
2115 double s,
2116 double smallest,
2117 std::complex<double>* w)
2118 {
2119 const auto size {[&t](lapack_index i, lapack_index k) {
2120 return k == i + 1 ? std::abs(t(i, i)) : std::abs(t(i, i)) + (std::sqrt(std::abs(t(i + 1, i))) * std::sqrt(std::abs(t(i, i + 1))));
2121 }};
2122 lapack_index ns {jw};
2123 lapack_index ilst {1};
2124 while (ilst <= ns) {
2125 const lapack_index order {ns > 1 && t(ns, ns - 1) != 0.0 ? 2 : 1};
2126 double foo {order == 1 ? std::abs(t(ns, ns)) : size(ns - 1, ns + 1)};
2127 if (foo == 0.0) {
2128 foo = std::abs(s);
2129 }
2130 const double spike {order == 1 ? std::abs(s * vt(ns, 1)) : std::max(std::abs(s * vt(ns, 1)), std::abs(s * vt(ns - 1, 1)))};
2131 if (spike <= std::max(smallest, schur_ulp * foo)) {
2132 ns -= order;
2133 }
2134 else {
2135 (void)reorder(t, vt, jw, ns, ilst);
2136 ilst += order;
2137 }
2138 }
2139 if (ns < jw) { // bubble sort of the undeflated blocks, larger first
2140 bool sorted {false};
2141 lapack_index i {ns + 1};
2142 const auto next {[&t](lapack_index from, lapack_index end) { return from == end || t(from + 1, from) == 0.0 ? from + 1 : from + 2; }};
2143 while (!sorted) {
2144 sorted = true;
2145 const lapack_index kend {i - 1};
2146 i = 1;
2147 lapack_index k {next(i, ns)};
2148 while (k <= kend) {
2149 const double evi {size(i, k)};
2150 const double evk {k == kend ? std::abs(t(k, k)) : size(k, next(k, kend))};
2151 if (evi >= evk) {
2152 i = k;
2153 }
2154 else {
2155 sorted = false;
2156 lapack_index last {k};
2157 i = reorder(t, vt, jw, i, last) ? last : k;
2158 }
2159 k = next(i, kend);
2160 }
2161 }
2162 }
2163 for (lapack_index i {jw}; i >= 1;) {
2164 if (i == 1 || t(i, i - 1) == 0.0) {
2165 w[i - 1] = std::complex<double> {t(i, i), 0.0};
2166 --i;
2167 }
2168 else {
2169 double a {t(i - 1, i - 1)};
2170 double b {t(i - 1, i)};
2171 double c {t(i, i - 1)};
2172 double d {t(i, i)};
2173 const standard_block block {standardize_block(a, b, c, d)};
2174 w[i - 2] = std::complex<double> {block.re1, block.im1};
2175 w[i - 1] = std::complex<double> {block.re2, block.im2};
2176 i -= 2;
2177 }
2178 }
2179 return ns;
2180 }
2181
2183 inline lapack_index undeflated(one_based<std::complex<double>> t,
2184 one_based<std::complex<double>> vt,
2186 const std::complex<double>& s,
2187 double smallest,
2188 std::complex<double>* w)
2189 {
2190 lapack_index ns {jw};
2191 lapack_index ilst {1};
2192 for (lapack_index count {1}; count <= jw; ++count) {
2193 double foo {modulus_1(t(ns, ns))};
2194 if (foo == 0.0) {
2195 foo = modulus_1(s);
2196 }
2197 if (modulus_1(s) * modulus_1(vt(ns, 1)) <= std::max(smallest, schur_ulp * foo)) {
2198 --ns;
2199 }
2200 else {
2201 (void)reorder(t, vt, jw, ns, ilst);
2202 ++ilst;
2203 }
2204 }
2205 if (ns < jw) {
2206 for (lapack_index i {1}; i <= ns; ++i) {
2207 lapack_index first {i};
2208 for (lapack_index j {i + 1}; j <= ns; ++j) {
2209 first = modulus_1(t(j, j)) > modulus_1(t(first, first)) ? j : first;
2210 }
2211 lapack_index last {i};
2212 (void)reorder(t, vt, jw, first, last);
2213 }
2214 }
2215 for (lapack_index i {1}; i <= jw; ++i) {
2216 w[i - 1] = t(i, i);
2217 }
2218 return ns;
2219 }
2220
2227
2236 template <typename T>
2241 std::complex<double>* w,
2242 bool recursive)
2243 {
2244 const lapack_index n {static_cast<lapack_index>(target.n)};
2245 const one_based<T> h {target.h->data(), n};
2246 const double smallest {std::numeric_limits<double>::min() * (static_cast<double>(n) / schur_ulp)};
2247 const lapack_index jw {std::min(nw, kbot - ktop + 1)};
2248 const lapack_index kwtop {kbot - jw + 1};
2249 T s {kwtop == ktop ? T {} : h(kwtop, kwtop - 1)};
2250 if (kbot == kwtop) { // a 1 × 1 window: the whole active block (a window has two rows otherwise), so s = 0
2251 w[kwtop - 1] = h(kwtop, kwtop);
2252 return {0, 1};
2253 }
2254 const std::size_t width {count_of(jw)};
2255 buffer<T> t_store(width * width, T {});
2256 buffer<T> vt_store(width * width, T {});
2257 const one_based<T> t {t_store.data(), jw};
2258 const one_based<T> vt {vt_store.data(), jw};
2259 for (lapack_index i {1}; i <= jw; ++i) {
2260 for (lapack_index j {std::max<lapack_index>(1, i - 1)}; j <= jw; ++j) {
2261 t(i, j) = h(kwtop + i - 1, kwtop + j - 1);
2262 }
2263 vt(i, i) = T {1};
2264 }
2266 const schur_target<T> window {&t_store, width, 0, width, true, &vt_store};
2267 if (recursive && jw >= multishift_from(T {})) {
2268 multishift_schur(window, local.data(), false);
2269 }
2270 else {
2271 plain_schur(window, local.data());
2272 }
2273 for (lapack_index j {1}; j + 3 <= jw; ++j) { // the exchanges need a clean margin below the diagonal
2274 t(j + 2, j) = T {};
2275 t(j + 3, j) = T {};
2276 }
2277 if (jw > 2) {
2278 t(jw, jw - 2) = T {};
2279 }
2280 const lapack_index ns {undeflated(t, vt, jw, s, smallest, w + kwtop - 1)};
2281 if (ns == 0) {
2282 s = T {};
2283 }
2284 if (ns == jw) { // nothing deflated (s ≠ 0 then: s = 0 deflates everything): H as it was
2285 return {ns, 0};
2286 }
2287 if (ns > 1) { // the spike reflected back into the first column, then Hessenberg form above
2288 buffer<T> v(count_of(ns));
2289 for (lapack_index i {1}; i <= ns; ++i) {
2290 v[count_of(i - 1)] = conj_of(vt(i, 1));
2291 }
2292 T beta {v[0]};
2293 const T tau {reflector_tau(beta, v.data() + 1, count_of(ns - 1))};
2294 v[0] = T {1};
2295 for (lapack_index i {3}; i <= jw; ++i) {
2296 for (lapack_index j {1}; j + 2 <= i; ++j) {
2297 t(i, j) = T {};
2298 }
2299 }
2300 for (lapack_index c {1}; c <= jw; ++c) { // (I − τ̄ v vᴴ) on rows 1 … ns
2301 T sum {};
2302 for (lapack_index r {1}; r <= ns; ++r) {
2303 sum += times(conj_of(v[count_of(r - 1)]), t(r, c));
2304 }
2305 for (lapack_index r {1}; r <= ns; ++r) {
2306 t(r, c) -= times(times(conj_of(tau), v[count_of(r - 1)]), sum);
2307 }
2308 }
2309 for (lapack_index r {1}; r <= ns; ++r) { // (I − τ v vᴴ) on columns 1 … ns
2310 T sum {};
2311 for (lapack_index c {1}; c <= ns; ++c) {
2312 sum += times(t(r, c), v[count_of(c - 1)]);
2313 }
2314 for (lapack_index c {1}; c <= ns; ++c) {
2315 t(r, c) -= times(times(tau, sum), conj_of(v[count_of(c - 1)]));
2316 }
2317 }
2318 for (lapack_index c {1}; c <= jw; ++c) { // V ← V (I − τ v vᴴ): Vᵀ's rows 1 … ns
2319 T sum {};
2320 for (lapack_index r {1}; r <= ns; ++r) {
2321 sum += times(v[count_of(r - 1)], vt(r, c));
2322 }
2323 for (lapack_index r {1}; r <= ns; ++r) {
2324 vt(r, c) -= times(times(tau, conj_of(v[count_of(r - 1)])), sum);
2325 }
2326 }
2327 const buffer<T> qt {hessenberg(t_store, width, 0, count_of(ns), true)};
2328 buffer<T> rows(count_of(ns) * width, T {});
2329 gemm(count_of(ns), width, count_of(ns), qt.data(), width, vt_store.data(), width, rows.data(), width);
2330 std::copy(rows.begin(), rows.end(), vt_store.begin());
2331 }
2332 if (kwtop > 1) {
2333 h(kwtop, kwtop - 1) = s * conj_of(vt(1, 1));
2334 }
2335 for (lapack_index i {1}; i <= jw; ++i) {
2336 for (lapack_index j {std::max<lapack_index>(1, i - 1)}; j <= jw; ++j) {
2337 h(kwtop + i - 1, kwtop + j - 1) = t(i, j);
2338 }
2339 }
2340 buffer<T> v(width * width);
2341 buffer<T> v_adjoint(width * width);
2342 for (lapack_index i {1}; i <= jw; ++i) {
2343 for (lapack_index j {1}; j <= jw; ++j) {
2344 v[count_of(((j - 1) * jw) + (i - 1))] = vt(i, j);
2345 v_adjoint[count_of(((i - 1) * jw) + (j - 1))] = conj_of(vt(i, j));
2346 }
2347 }
2348 const lapack_index ltop {target.full ? 1 : ktop};
2349 if (ltop < kwtop) { // the rows above the window: H V
2350 const std::size_t rows {count_of(kwtop - ltop)};
2351 buffer<T> product(rows * width, T {});
2352 gemm(rows, width, width, &h(ltop, kwtop), count_of(n), v.data(), width, product.data(), width);
2353 for (std::size_t r {0}; r < rows; ++r) {
2354 std::copy_n(product.data() + (r * width), width, &h(ltop + static_cast<lapack_index>(r), kwtop));
2355 }
2356 }
2357 if (target.full && kbot < n) { // the columns right of it: Vᴴ H
2358 const std::size_t columns {count_of(n - kbot)};
2359 buffer<T> product(width * columns, T {});
2360 gemm(width, columns, width, v_adjoint.data(), width, &h(kwtop, kbot + 1), count_of(n), product.data(), columns);
2361 for (std::size_t r {0}; r < width; ++r) {
2362 std::copy_n(product.data() + (r * columns), columns, &h(kwtop + static_cast<lapack_index>(r), kbot + 1));
2363 }
2364 }
2365 if (target.zt != nullptr) { // (Z V)ᵀ = Vᵀ Zᵀ
2366 const one_based<T> zt {target.zt->data(), n};
2367 const lapack_index low {static_cast<lapack_index>(target.low) + 1};
2368 const std::size_t columns {target.high - target.low};
2369 buffer<T> product(width * columns, T {});
2370 gemm(width, columns, width, vt_store.data(), width, &zt(kwtop, low), count_of(n), product.data(), columns);
2371 for (std::size_t r {0}; r < width; ++r) {
2372 std::copy_n(product.data() + (r * columns), columns, &zt(kwtop + static_cast<lapack_index>(r), low));
2373 }
2374 }
2375 return {ns, jw - ns};
2376 }
2377
2380 {
2381 lapack_index ns {2};
2382 if (nh >= 6000) {
2383 ns = 256;
2384 }
2385 else if (nh >= 3000) {
2386 ns = 128;
2387 }
2388 else if (nh >= 590) {
2389 ns = 64;
2390 }
2391 else if (nh >= 150) {
2392 ns = std::max<lapack_index>(10, nh / static_cast<lapack_index>(std::lround(std::log2(static_cast<double>(nh)))));
2393 }
2394 else if (nh >= 60) {
2395 ns = 10;
2396 }
2397 else if (nh >= 30) {
2398 ns = 4;
2399 }
2400 return std::max<lapack_index>(2, ns - (ns % 2));
2401 }
2402
2406 {
2407 return nh <= 500 ? ns : 3 * ns / 2;
2408 }
2409
2413 std::complex<double>* w,
2417 {
2418 for (lapack_index i {kbot}; i >= std::max(ks + 1, ktop + 2); i -= 2) {
2419 const double ss {std::abs(h(i, i - 1)) + std::abs(h(i - 1, i - 2))};
2420 double a {(0.75 * ss) + h(i, i)};
2421 double b {ss};
2422 double c {-0.4375 * ss};
2423 double d {a};
2424 const standard_block block {standardize_block(a, b, c, d)};
2425 w[i - 2] = std::complex<double> {block.re1, block.im1};
2426 w[i - 1] = std::complex<double> {block.re2, block.im2};
2427 }
2428 // LAPACK's case ks = ktop cannot arise: a sweep's active block has three rows at least (one of two deflates whole
2429 // in its window), and ns ≤ kbot − ktop.
2430 }
2431
2433 inline void exceptional_shifts(one_based<std::complex<double>> h,
2434 std::complex<double>* w,
2437 lapack_index /*ktop*/)
2438 {
2439 for (lapack_index i {kbot}; i >= ks + 1; i -= 2) {
2440 w[i - 1] = h(i, i) + (0.75 * modulus_1(h(i, i - 1)));
2441 w[i - 2] = w[i - 1];
2442 }
2443 }
2444
2447 inline void pair_for_sweep(std::complex<double>* w,
2450 double /*real*/)
2451 {
2452 for (lapack_index i {kbot}; i >= ks + 2; i -= 2) {
2453 if (w[i - 1].imag() != -w[i - 2].imag()) {
2454 const std::complex<double> last {w[i - 1]};
2455 w[i - 1] = w[i - 2];
2456 w[i - 2] = w[i - 3];
2457 w[i - 3] = last;
2458 }
2459 }
2460 }
2461
2463 inline void pair_for_sweep(std::complex<double>* /*w*/,
2464 lapack_index /*ks*/,
2465 lapack_index /*kbot*/,
2466 const std::complex<double>& /*complex*/)
2467 {
2468 }
2469
2471 [[nodiscard]] inline bool two_real_shifts(const std::complex<double>* w,
2474 double /*real*/)
2475 {
2476 return kbot - ks + 1 == 2 && w[kbot - 1].imag() == 0.0;
2477 }
2478
2480 [[nodiscard]] inline bool two_real_shifts(const std::complex<double>* /*w*/,
2483 const std::complex<double>& /*complex*/)
2484 {
2485 return kbot - ks + 1 == 2;
2486 }
2487
2501 template <typename T>
2503 std::complex<double>* w,
2504 bool recursive,
2506 {
2507 const lapack_index n {static_cast<lapack_index>(target.n)};
2508 const one_based<T> h {target.h->data(), n};
2509 const lapack_index ilo {static_cast<lapack_index>(target.low) + 1};
2510 const lapack_index ihi {static_cast<lapack_index>(target.high)};
2511 const lapack_index nh {ihi - ilo + 1};
2513 const bool accumulate {recommended >= 14};
2514 const lapack_index nwr {std::min({nh, (n - 1) / 3, std::max<lapack_index>(2, recommended_window(nh, recommended))})};
2515 lapack_index nsr {std::min({recommended, (n - 3) / 6, ihi - ilo})};
2516 nsr = std::max<lapack_index>(2, nsr - (nsr % 2));
2517 const lapack_index nwmax {(n - 1) / 3};
2518 const lapack_index nsmax {((n - 3) / 6) - (((n - 3) / 6) % 2)};
2520 lapack_index ndfl {1};
2521 lapack_index ndec {-1};
2523 const lapack_index itmax {steps > 0 ? steps : 30 * std::max<lapack_index>(10, nh)};
2524 for (lapack_index it {1}; it <= itmax; ++it) {
2525 if (kbot < ilo) {
2526 return;
2527 }
2529 while (ktop > ilo && h(ktop, ktop - 1) != T {}) {
2530 --ktop;
2531 }
2532 const lapack_index active {kbot - ktop + 1};
2533 const lapack_index nwupbd {std::min(active, nwmax)};
2534 nw = ndfl < window_growth ? std::min(nwupbd, nwr) : std::min(nwupbd, 2 * nw);
2535 if (nw < nwmax) {
2536 if (nw >= active - 1) {
2537 nw = active;
2538 }
2539 else if (modulus_1(h(kbot - nw + 1, kbot - nw)) > modulus_1(h(kbot - nw, kbot - nw - 1))) {
2540 ++nw;
2541 }
2542 }
2543 if (ndfl < window_growth) {
2544 ndec = -1;
2545 }
2546 else if (ndec >= 0 || nw >= nwupbd) {
2547 ++ndec;
2548 if (nw - ndec < 2) {
2549 ndec = 0;
2550 }
2551 nw -= ndec;
2552 }
2554 kbot -= found.deflated;
2555 lapack_index ks {kbot - found.shifts + 1};
2556 if (found.deflated == 0 || (100 * found.deflated <= nw * nibble && kbot - ktop + 1 > std::min(multishift_crossover, nwmax))) {
2557 lapack_index ns {std::min({nsmax, nsr, std::max<lapack_index>(2, kbot - ktop)})};
2558 ns -= ns % 2;
2559 if (ndfl % exceptional_after == 0) {
2560 ks = kbot - ns + 1;
2562 }
2563 else {
2564 if (kbot - ks + 1 <= ns / 2) { // too few: the eigenvalues of the trailing ns × ns
2565 ks = kbot - ns + 1;
2566 buffer<T> trailing(count_of(ns * ns), T {});
2567 const one_based<T> lower {trailing.data(), ns};
2568 for (lapack_index i {1}; i <= ns; ++i) {
2569 for (lapack_index j {std::max<lapack_index>(1, i - 1)}; j <= ns; ++j) {
2570 lower(i, j) = h(ks + i - 1, ks + j - 1);
2571 }
2572 }
2573 // by the single-shift iterations: LAPACK's dlaqr4 serves only past order 3000, where ns exceeds 75
2574 plain_schur(schur_target<T> {&trailing, count_of(ns), 0, count_of(ns), false, nullptr}, w + ks - 1);
2575 }
2576 if (kbot - ks + 1 > ns) { // bubble sort, larger first: a real matrix's pairs stay together
2577 bool sorted {false};
2578 for (lapack_index k {kbot}; k > ks && !sorted; --k) {
2579 sorted = true;
2580 for (lapack_index i {ks}; i < k; ++i) {
2581 if (modulus_1(w[i - 1]) < modulus_1(w[i])) {
2582 std::swap(w[i - 1], w[i]);
2583 sorted = false;
2584 }
2585 }
2586 }
2587 }
2588 pair_for_sweep(w, ks, kbot, T {});
2589 }
2590 if (two_real_shifts(w, ks, kbot, T {})) {
2591 // two shifts, real for a real matrix: the one nearer the corner, twice
2592 const T corner {h(kbot, kbot)};
2593 if (modulus_1(as_scalar(w[kbot - 1], T {}) - corner) < modulus_1(as_scalar(w[kbot - 2], T {}) - corner)) {
2594 w[kbot - 2] = w[kbot - 1];
2595 }
2596 else {
2597 w[kbot - 1] = w[kbot - 2];
2598 }
2599 }
2600 ns = std::min(ns, kbot - ks + 1);
2601 ns -= ns % 2;
2602 ks = kbot - ns + 1;
2604 }
2605 ndfl = found.deflated > 0 ? 1 : ndfl + 1;
2606 }
2607 throw convergence_error("eigendecomposition: the QR iterations did not converge");
2608 }
2609
2612 inline void schur_form(const schur_target<double>& t,
2613 buffer<double>& re,
2614 buffer<double>& im,
2615 std::size_t steps)
2616 {
2617 if (t.high - t.low < count_of(multishift_from(0.0))) {
2618 real_schur(t, re, im, steps);
2619 return;
2620 }
2622 multishift_schur(t, w.data(), true);
2623 for (std::size_t i {t.low}; i < t.high; ++i) {
2624 re[i] = w[i].real();
2625 im[i] = w[i].imag();
2626 }
2627 }
2628
2630 inline void schur_form(const schur_target<std::complex<double>>& t,
2631 buffer<std::complex<double>>& values,
2632 std::size_t steps)
2633 {
2634 if (t.high - t.low < count_of(multishift_from(std::complex<double> {}))) {
2635 complex_schur(t, values, steps);
2636 return;
2637 }
2638 multishift_schur(t, values.data(), true);
2639 }
2640
2646 template <typename S>
2647 [[nodiscard]] double solve_block(const S (&m)[4],
2648 S (&r)[2],
2649 double floor)
2650 {
2651 std::size_t p {0};
2652 for (std::size_t q {1}; q < 4; ++q) {
2653 p = modulus_1(m[q]) > modulus_1(m[p]) ? q : p;
2654 }
2655 const std::size_t pr {p / 2};
2656 const std::size_t pc {p % 2};
2657 const S u11 {modulus_1(m[p]) < floor ? S {floor} : m[p]};
2658 const S l21 {m[((1 - pr) * 2) + pc] / u11};
2659 const S u12 {m[(pr * 2) + 1 - pc]};
2660 S u22 {m[((1 - pr) * 2) + 1 - pc] - (l21 * u12)};
2661 if (modulus_1(u22) < floor) {
2662 u22 = S {floor};
2663 }
2664 const double rmax {std::max(modulus_1(r[0]), modulus_1(r[1]))};
2665 const double pivot {std::min(modulus_1(u11), modulus_1(u22))};
2666 double factor {1.0};
2667 if (4.0 * rmax > back_substitution_limit * pivot) {
2668 factor = back_substitution_limit * pivot / (4.0 * rmax);
2669 r[0] *= factor;
2670 r[1] *= factor;
2671 }
2672 const S rp {r[pr]};
2673 const S ro {r[1 - pr] - (l21 * rp)};
2674 r[1 - pc] = ro / u22;
2675 r[pc] = (rp - (u12 * r[1 - pc])) / u11;
2676 return factor;
2677 }
2678
2681 template <typename S>
2683 {
2685 double factor;
2686 };
2687
2690 template <typename S, typename P>
2692 P pivot,
2693 double floor)
2694 {
2695 const double size {modulus_1(pivot) < floor ? floor : modulus_1(pivot)};
2696 const P divisor {modulus_1(pivot) < floor ? P {floor} : pivot};
2697 double factor {1.0};
2698 if (modulus_1(r) > back_substitution_limit * size) {
2699 factor = back_substitution_limit * size / (2.0 * modulus_1(r));
2700 r *= factor;
2701 }
2702 return {r / divisor, factor};
2703 }
2704
2707 {
2708 using scalar = double;
2709
2711
2713 [[nodiscard]] double dot(const double* row,
2714 std::size_t from,
2715 std::size_t to) const
2716 {
2717 return dot_product(row + from, x.data() + from, to - from + 1);
2718 }
2719
2721 void set(std::size_t j,
2722 double value)
2723 {
2724 x[j] = value;
2725 }
2726
2728 void scale(std::size_t from,
2729 std::size_t to,
2730 double factor)
2731 {
2732 for (std::size_t l {from}; l <= to; ++l) {
2733 x[l] *= factor;
2734 }
2735 }
2736 };
2737
2741 {
2742 using scalar = std::complex<double>;
2743
2746
2748 [[nodiscard]] std::complex<double> dot(const double* row,
2749 std::size_t from,
2750 std::size_t to) const
2751 {
2752 return {dot_product(row + from, re.data() + from, to - from + 1), dot_product(row + from, im.data() + from, to - from + 1)};
2753 }
2754
2756 void set(std::size_t j,
2757 const std::complex<double>& value)
2758 {
2759 re[j] = value.real();
2760 im[j] = value.imag();
2761 }
2762
2764 void scale(std::size_t from,
2765 std::size_t to,
2766 double factor)
2767 {
2768 for (std::size_t l {from}; l <= to; ++l) {
2769 re[l] *= factor;
2770 im[l] *= factor;
2771 }
2772 }
2773 };
2774
2779 {
2780 using scalar = std::complex<double>;
2781
2784
2786 [[nodiscard]] std::complex<double> dot(const std::complex<double>* row,
2787 std::size_t from,
2788 std::size_t to) const
2789 {
2790 const auto* const parts {reinterpret_cast<const double*>(row)}; // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast): the layout guarantee above
2791 const std::size_t count {2 * (to - from + 1)};
2792 return {dot_product(parts + (2 * from), real_side.data() + (2 * from), count),
2793 dot_product(parts + (2 * from), imaginary_side.data() + (2 * from), count)};
2794 }
2795
2797 void set(std::size_t j,
2798 const std::complex<double>& value)
2799 {
2800 real_side[2 * j] = value.real();
2801 real_side[(2 * j) + 1] = -value.imag();
2802 imaginary_side[2 * j] = value.imag();
2803 imaginary_side[(2 * j) + 1] = value.real();
2804 }
2805
2807 [[nodiscard]] std::complex<double> get(std::size_t j) const
2808 {
2809 return {real_side[2 * j], imaginary_side[2 * j]};
2810 }
2811
2813 void scale(std::size_t from,
2814 std::size_t to,
2815 double factor)
2816 {
2817 for (std::size_t l {2 * from}; l <= (2 * to) + 1; ++l) {
2818 real_side[l] *= factor;
2819 imaginary_side[l] *= factor;
2820 }
2821 }
2822 };
2823
2826 inline void normalize_vector(std::complex<double>* v,
2827 std::size_t n,
2828 bool signed_real)
2829 {
2830 const double norm {scaled_norm(v, n)};
2831 std::size_t largest {0};
2832 for (std::size_t r {0}; r < n; ++r) {
2833 v[r] /= norm;
2834 largest = std::norm(v[r]) > std::norm(v[largest]) ? r : largest;
2835 }
2836 std::complex<double> turn {std::conj(v[largest]) / std::abs(v[largest])};
2837 if (signed_real && std::signbit(v[largest].real())) {
2838 turn = -turn;
2839 }
2840 for (std::size_t r {0}; r < n; ++r) {
2841 v[r] = times(v[r], turn);
2842 }
2843 v[largest] = std::complex<double> {v[largest].real(), 0.0};
2844 }
2845
2852 template <typename T, typename X>
2854 std::size_t n,
2855 std::size_t k,
2856 std::size_t top,
2857 typename X::scalar lambda,
2858 double floor,
2859 X& x)
2860 {
2861 using S = typename X::scalar;
2862 for (std::size_t j {k}; j > 0;) {
2863 if constexpr (std::is_floating_point_v<T>) {
2864 if (j >= 2 && t[((j - 1) * n) + j - 2] != 0.0) { // a 2 × 2 block, rows j − 2 and j − 1
2865 const S m[4] {S {t[((j - 2) * n) + j - 2]} - lambda, S {t[((j - 2) * n) + j - 1]}, S {t[((j - 1) * n) + j - 2]},
2866 S {t[((j - 1) * n) + j - 1]} - lambda};
2867 S r[2] {-x.dot(t.data() + ((j - 2) * n), j, top), -x.dot(t.data() + ((j - 1) * n), j, top)};
2868 const double factor {solve_block(m, r, floor)};
2869 if (factor != 1.0) {
2870 x.scale(j, top, factor);
2871 }
2872 x.set(j - 2, r[0]);
2873 x.set(j - 1, r[1]);
2874 j -= 2;
2875 continue;
2876 }
2877 }
2878 const auto [value, factor] {divided(-x.dot(t.data() + ((j - 1) * n), j, top), S {t[((j - 1) * n) + j - 1]} - lambda, floor)};
2879 if (factor != 1.0) {
2880 x.scale(j, top, factor);
2881 }
2882 x.set(j - 1, value);
2883 --j;
2884 }
2885 }
2886
2891 [[nodiscard]] inline buffer<std::complex<double>> complex_vectors(const buffer<std::complex<double>>& t,
2892 std::size_t n)
2893 {
2894 using C = std::complex<double>;
2895 const double smallest {std::numeric_limits<double>::min() * (static_cast<double>(n) / schur_ulp)};
2896 buffer<C> xt(n * n, C {});
2897 interleaved_components x {buffer<double>(2 * n, 0.0), buffer<double>(2 * n, 0.0)};
2898 for (std::size_t k {0}; k < n; ++k) {
2899 const C lambda {t[(k * n) + k]};
2900 x.set(k, C {1.0});
2901 solve_upward(t, n, k, k, lambda, std::max(schur_ulp * modulus_1(lambda), smallest), x);
2902 for (std::size_t l {0}; l <= k; ++l) {
2903 xt[(k * n) + l] = x.get(l);
2904 }
2905 }
2906 return xt;
2907 }
2908
2915
2922 const buffer<double>& re,
2923 const buffer<double>& im,
2924 std::size_t n)
2925 {
2926 using C = std::complex<double>;
2927 const double smallest {std::numeric_limits<double>::min() * (static_cast<double>(n) / schur_ulp)};
2928 real_eigenvectors out {buffer<double>(n * n, 0.0), buffer<double>(n * n, 0.0)};
2929 real_components x {buffer<double>(n, 0.0)};
2931 for (std::size_t k {0}; k < n; ++k) {
2932 const double floor {std::max(schur_ulp * (std::abs(re[k]) + std::abs(im[k])), smallest)};
2933 double* row_re {out.re.data() + (k * n)};
2934 double* row_im {out.im.data() + (k * n)};
2935 if (im[k] == 0.0) {
2936 x.set(k, 1.0);
2937 solve_upward(t, n, k, k, re[k], floor, x);
2938 std::copy(x.x.begin(), x.x.begin() + static_cast<std::ptrdiff_t>(k + 1), row_re);
2939 }
2940 else if (im[k] > 0.0) { // the pair's block is rows k, k + 1
2941 const double b {t[(k * n) + k + 1]};
2942 const double c {t[((k + 1) * n) + k]};
2943 y.set(k, std::abs(b) >= std::abs(c) ? C {1.0} : C {-im[k] / c});
2944 y.set(k + 1, std::abs(b) >= std::abs(c) ? C {0.0, im[k] / b} : C {0.0, 1.0});
2945 solve_upward(t, n, k, k + 1, C {re[k], im[k]}, floor, y);
2946 for (std::size_t l {0}; l <= k + 1; ++l) {
2947 row_re[l] = y.re[l];
2948 row_im[l] = y.im[l];
2949 row_re[l + n] = y.re[l];
2950 row_im[l + n] = -y.im[l];
2951 }
2952 }
2953 }
2954 return out;
2955 }
2956
2958 template <typename T>
2960 const buffer<T>& zt,
2961 std::size_t n)
2962 {
2963 buffer<T> vt(n * n, T {});
2964 if (n > 0) {
2965 gemm(n, n, n, xt.data(), n, zt.data(), n, vt.data(), n);
2966 }
2967 return vt;
2968 }
2969 } // namespace detail
2970
2978 template <typename T>
2980 {
2981 public:
2982
2992 bool vectors = true)
2993 : n_(input.rows())
2994 {
2995 if (input.cols() != n_) {
2996 throw dimension_error("eigendecomposition requires a square matrix");
2997 }
2998 detail::require_finite(input, "eigendecomposition");
2999 double largest {0.0}; // the largest part of an entry: within √2 of its modulus, and no hypot per entry
3000 for (std::size_t i {0}; i < n_; ++i) {
3001 for (std::size_t j {0}; j < n_; ++j) {
3002 largest = std::max(largest, detail::largest_part(input(i, j)));
3003 }
3004 }
3005 // A power of two near the largest magnitude: dividing by it is exact, and keeps the iterations' sums of squares
3006 // and the back-substitution's growth in range.
3007 const double scale {largest == 0.0 ? 1.0 : std::ldexp(1.0, std::ilogb(largest))};
3008 buffer<T> a(n_ * n_, T {});
3009 for (std::size_t i {0}; i < n_; ++i) {
3010 for (std::size_t j {0}; j < n_; ++j) {
3011 a[(i * n_) + j] = input(i, j) / detail::from_double<T>(scale);
3012 }
3013 }
3016 const std::size_t steps {detail::schur_steps_per_order * std::max<std::size_t>(10, balanced.high - balanced.low)};
3017 // The whole Schur form past the multishift crossover even for the eigenvalues alone: the sweeps' products then
3018 // have the same shapes as with the vectors, and the eigenvalues the same bits (2–4 % of the iterations from order
3019 // 256 on); below it the single-shift iterations give them either way.
3020 const bool whole {vectors || balanced.high - balanced.low >= detail::count_of(detail::multishift_from(T {}))};
3021 const detail::schur_target<T> target {&a, n_, balanced.low, balanced.high, whole, vectors ? &z : nullptr}; // z is Zᵀ
3023 buffer<std::complex<double>> vt; // Vᵀ: each vector a row
3024 if constexpr (std::is_floating_point_v<T>) {
3025 buffer<double> re(n_, 0.0);
3026 buffer<double> im(n_, 0.0);
3027 for (std::size_t i {0}; i < n_; ++i) {
3028 re[i] = a[(i * n_) + i]; // the isolated ones; the active block's are overwritten
3029 }
3031 for (std::size_t i {0}; i < n_; ++i) {
3032 values[i] = std::complex<double> {re[i] * scale, im[i] * scale};
3033 real_ = real_ && im[i] == 0.0;
3034 }
3035 if (vectors) {
3040 for (std::size_t e {0}; e < n_ * n_; ++e) {
3041 vt[e] = std::complex<double> {vre[e], vim[e]};
3042 }
3043 }
3044 }
3045 else {
3046 for (std::size_t i {0}; i < n_; ++i) {
3047 values[i] = a[(i * n_) + i];
3048 }
3050 for (std::size_t i {0}; i < n_; ++i) {
3051 values[i] *= scale;
3052 }
3053 real_ = false;
3054 if (vectors) {
3056 }
3057 }
3059 if (!vectors) {
3060 return;
3061 }
3062 for (std::size_t c {0}; c < n_; ++c) { // Vᵀ ← Vᵀ D, then the exchanges undone, on its columns
3063 std::complex<double>* row {vt.data() + (c * n_)};
3064 for (std::size_t i {balanced.low}; i < balanced.high; ++i) {
3065 row[i] *= balanced.scale[i];
3066 }
3067 for (std::size_t s {balanced.swaps.size()}; s-- > 0;) {
3068 std::swap(row[balanced.swaps[s].first], row[balanced.swaps[s].second]);
3069 }
3070 finish(row, values_[c], T {});
3071 }
3073 constexpr std::size_t tile {32}; // the transpose by tiles: a column of vt read whole per cache line
3074 for (std::size_t r0 {0}; r0 < n_; r0 += tile) {
3075 for (std::size_t c0 {0}; c0 < n_; c0 += tile) {
3076 for (std::size_t r {r0}; r < std::min(r0 + tile, n_); ++r) {
3077 for (std::size_t c {c0}; c < std::min(c0 + tile, n_); ++c) {
3078 v[(r * n_) + c] = vt[(c * n_) + r];
3079 }
3080 }
3081 }
3082 }
3083 vectors_ = matrix<std::complex<double>> {n_, n_, std::move(v)};
3084 }
3085
3088 {
3089 return values_;
3090 }
3091
3094 {
3095 return vectors_;
3096 }
3097
3100 {
3101 return std::move(vectors_);
3102 }
3103
3107 {
3108 return real_;
3109 }
3110
3111 private:
3112
3116 void finish(std::complex<double>* row,
3117 const std::complex<double>& value,
3118 double /*real*/) const
3119 {
3120 if (value.imag() == 0.0) {
3121 const double norm {detail::scaled_norm(row, n_)};
3122 for (std::size_t r {0}; r < n_; ++r) {
3123 row[r] = std::complex<double> {row[r].real() / norm, 0.0};
3124 }
3125 }
3126 else if (value.imag() < 0.0) {
3127 const std::complex<double>* first {row - n_};
3128 for (std::size_t r {0}; r < n_; ++r) {
3129 row[r] = std::conj(first[r]);
3130 }
3131 }
3132 else {
3134 }
3135 }
3136
3139 void finish(std::complex<double>* row,
3140 const std::complex<double>& /*value*/,
3141 const std::complex<double>& /*complex*/) const
3142 {
3144 }
3145
3146 std::size_t n_;
3149 bool real_ {true};
3150 };
3151
3158 template <typename T>
3160 {
3161 return eigen_decomposition<T> {a, false}.values();
3162 }
3163} // namespace numerical
3164
3165#endif // NUMERICAL_EIGEN_HPP
Thrown when an iterative algorithm has not converged within its bound of sweeps.
Thrown when an operation's operands have incompatible shapes.
Definition vector.hpp:35
The eigenvalues, and optionally the right eigenvectors, of a general square matrix,...
Definition eigen.hpp:2980
void finish(std::complex< double > *row, const std::complex< double > &value, double) const
Normalises the eigenvector row (a row of Vᵀ) of a real matrix's eigenvalue value, as dgeev: a real on...
Definition eigen.hpp:3116
matrix< std::complex< double > > vectors_
The eigenvectors as columns.
Definition eigen.hpp:3148
bool real_
Whether all eigenvalues are real.
Definition eigen.hpp:3149
const vector< std::complex< double > > & values() const noexcept
The eigenvalues, in the order of the Schur form.
Definition eigen.hpp:3087
const matrix< std::complex< double > > & vectors() const &noexcept
The eigenvectors as columns, ordered to match values (empty where they were not computed).
Definition eigen.hpp:3093
void finish(std::complex< double > *row, const std::complex< double > &, const std::complex< double > &) const
Normalises the eigenvector row of a complex matrix, as zgeev: to unit norm, its largest component rea...
Definition eigen.hpp:3139
bool real() const noexcept
Whether every eigenvalue is real (a real matrix's whose Schur form has no 2 × 2 block): its vectors a...
Definition eigen.hpp:3106
std::size_t n_
The matrix order.
Definition eigen.hpp:3146
vector< std::complex< double > > values_
The eigenvalues.
Definition eigen.hpp:3147
matrix< std::complex< double > > vectors() &&
The eigenvectors, moved out of an expiring decomposition.
Definition eigen.hpp:3099
eigen_decomposition(const matrix< T > &input, bool vectors=true)
Decomposes input.
Definition eigen.hpp:2991
A dense, row-major matrix of T with value semantics.
Definition matrix.hpp:35
A dense vector of T with value semantics.
Definition vector.hpp:86
Matrix factorisations and the algorithms built on them.
buffer< T > hessenberg_unblocked(buffer< T > &a, std::size_t n, std::size_t low, std::size_t high, bool form)
Reduces the active block low … high − 1 of the balanced row-major n × n a to upper Hessenberg form by...
Definition eigen.hpp:215
double rescale_tiny(buffer< T > &x)
Scales x by rescue_factor of its largest magnitude, exactly, so that a Householder vector built from ...
T reflector_tau(T &alpha, T *x, std::size_t count)
The Householder reflection H = I − τ v vᴴ, v = (1, v₁…), with Hᴴ (α, x) = (β, 0) and β real,...
Definition eigen.hpp:331
void reflect_columns_3(const std::array< double, 3 > &u, double tau, double *t, std::size_t ld, std::size_t count)
The same from the right, to the count × 3 columns at t.
Definition eigen.hpp:1359
constexpr double back_substitution_limit
The modulus an eigenvector's components are kept under during the back-substitution: the sums stay fi...
Definition eigen.hpp:50
void reflect_three_rows(T *r1, T *r2, T *r3, std::size_t from, std::size_t to, const T &tau, const T &v2, const T &v3, bool adjoint)
A reflection I − τ v vᴴ, v = (1, v₂, v₃), on the entries from … to − 1 of three rows: its adjoint fro...
Definition eigen.hpp:770
void bulge_column(one_based< double > h, lapack_index count, const std::complex< double > &s1, const std::complex< double > &s2, double *v)
A multiple of the first column of (H − s₁ I)(H − s₂ I) for the leading 2 × 2 or 3 × 3 (count) of the ...
Definition eigen.hpp:1714
void multishift_schur(const schur_target< T > &target, std::complex< double > *w, bool recursive, lapack_index steps=0)
The Schur form of target's active block by the multishift QR algorithm with aggressive early deflatio...
Definition eigen.hpp:2502
void normalize_vector(std::complex< double > *v, std::size_t n, bool signed_real)
Scales v to unit Euclidean norm and turns it so that its first component of largest modulus is real: ...
Definition eigen.hpp:2826
void reflect_two_rows(T *r1, T *r2, std::size_t from, std::size_t to, const T &tau, const T &v2, bool adjoint)
reflect_three_rows for a reflection of two entries, v = (1, v₂), on two rows: its own loop,...
Definition eigen.hpp:796
buffer< T > hessenberg_blocked(buffer< T > &a, std::size_t n, std::size_t low, std::size_t high, bool form)
hessenberg_unblocked by panels of hessenberg_block columns, as LAPACK's dgehrd and dlahr2: a panel's ...
Definition eigen.hpp:422
buffer< T > hessenberg(buffer< T > &a, std::size_t n, std::size_t low, std::size_t high, bool form)
hessenberg_blocked from an active order of hessenberg_crossover, else hessenberg_unblocked.
Definition eigen.hpp:601
void exchange(buffer< T > &a, std::size_t n, std::size_t i, std::size_t j)
Exchanges rows i and j, then columns i and j, of the row-major n × n a: a similarity.
Definition eigen.hpp:106
void reflect_three_columns(T *first, std::size_t rows, std::size_t ld, const T &tau, const T &v2, const T &v3)
A reflection I − τ v vᴴ, v = (1, v₂, v₃), from the right on rows rows (ld apart) of the three columns...
Definition eigen.hpp:820
constexpr std::size_t hessenberg_crossover
The active order from which hessenberg reduces by panels.
Definition eigen.hpp:309
std::size_t count_of(lapack_index count)
A count as an unsigned size (never negative where it is taken).
Definition eigen.hpp:1230
void reflect_two_columns(T *first, std::size_t rows, std::size_t ld, const T &tau, const T &v2)
The same for a reflection of two entries, v = (1, v₂), on two columns.
Definition eigen.hpp:840
void vigilant_deflation(one_based< T > h, lapack_index k, lapack_index ktop, lapack_index kbot, double smallest)
H(k + 1, k) set to zero where the deflation criterion of Ahues and Tisseur holds, as the sweep of dla...
Definition eigen.hpp:1824
void chase_bulges(const schur_target< T > &target, lapack_index ktop, lapack_index kbot, std::complex< double > *s, lapack_index count, bool accumulate)
One small-bulge multishift QR sweep over the active block ktop … kbot of target's Hessenberg matrix w...
Definition eigen.hpp:1882
double solve_block(const S(&m)[4], S(&r)[2], double floor)
Solves [a b; c d] x = r in place of r by Gaussian elimination with complete pivoting,...
Definition eigen.hpp:2647
constexpr std::size_t hessenberg_block
The columns of a panel of hessenberg_blocked.
Definition eigen.hpp:306
constexpr double schur_ulp
The relative spacing LAPACK's ‘dlamch('P’)` names: the roundoff a QR sweep judges a subdiagonal by.
Definition eigen.hpp:38
void pair_for_sweep(std::complex< double > *w, lapack_index ks, lapack_index kbot, double)
A real matrix's shifts brought into pairs, each a conjugate pair or two reals, as dlaqr0 does before ...
Definition eigen.hpp:2447
void require_finite(const matrix< T > &a, const char *what)
The one non-finite policy, checked where every decomposition starts.
void exchange_diagonal(one_based< T > t, one_based< T > qt, lapack_index n, lapack_index k)
Exchanges the diagonal entries k and k + 1 of the upper triangular (or quasi-triangular,...
Definition eigen.hpp:1326
void reflect_from_left(T *x, std::size_t stride, const T &tau, const T &v2, const T &v3)
The adjoint I − τ̄ v vᴴ of a reflection of three entries from the left on one column (x three entries...
Definition eigen.hpp:1861
T conj_of(const T &x)
The conjugate of a real (or general) scalar — the value itself.
real_operand operand_of(const double *v, std::size_t stride, std::size_t count)
The count entries stride apart at v as a real_operand.
Definition eigen.hpp:378
bool two_real_shifts(const std::complex< double > *w, lapack_index ks, lapack_index kbot, double)
Whether a real matrix's sweep is down to two shifts, both real: then the one nearer the corner serves...
Definition eigen.hpp:2471
void small_sylvester(const one_based< double > &tl, const one_based< double > &tr, const one_based< double > &b, lapack_index n1, lapack_index n2, double(&x)[4])
The solution X (n1 × n2, row-major in x) of TL X − X TR = B, n1 and n2 at most 2, by Gaussian elimina...
Definition eigen.hpp:1379
auto times(const A &a, const B &b)
a · b.
Definition eigen.hpp:67
lapack_index block_order(one_based< double > t, lapack_index n, lapack_index j)
The order of the diagonal block of the quasi-triangular t that starts at j: 2 where a subdiagonal ent...
Definition eigen.hpp:1590
bool exchange_blocks(one_based< double > t, one_based< double > qt, lapack_index n, lapack_index j1, lapack_index n1, lapack_index n2)
Exchanges the adjacent diagonal blocks of the real quasi-triangular t at j1, of orders n1 then n2,...
Definition eigen.hpp:1470
std::ptrdiff_t lapack_index
A signed index from 1, as LAPACK's: the multishift sweep starts its bulges in phantom columns above t...
Definition eigen.hpp:1206
buffer< T > back_transformed(const buffer< T > &xt, const buffer< T > &zt, std::size_t n)
Vᵀ = Xᵀ Zᵀ, row-major n × n, through the matrix product kernel.
Definition eigen.hpp:2959
bool reorder(one_based< double > t, one_based< double > qt, lapack_index n, lapack_index ifst, lapack_index &ilst)
Moves the diagonal block of the real quasi-triangular t at ifst to ilst by exchanges of adjacent bloc...
Definition eigen.hpp:1602
constexpr lapack_index multishift_crossover
LAPACK's nmin: the active order below which dlaqr0's iterations skip a sweep after enough deflation.
Definition eigen.hpp:1237
void gemm(std::size_t m, std::size_t n, std::size_t k, const T *a, std::size_t lda, const T *b, std::size_t ldb, T *c, std::size_t ldc)
C += A·B, every element of C summed in ascending order of the depth.
Definition gemm.hpp:2740
real_eigenvectors real_vectors(const buffer< double > &t, const buffer< double > &re, const buffer< double > &im, std::size_t n)
The eigenvectors of the real quasi-triangular t (row-major n × n, its 2 × 2 blocks standard) as the r...
Definition eigen.hpp:2921
void parallel_for(std::size_t count, std::size_t work_per_index, const Body &body)
Runs body(i) for every i in [0, count).
Definition parallel.hpp:580
bool already_reduced(double, double rest)
Whether (α, x), of tail norm rest, is already (β, 0) with β real: for a real one, its tail zero.
Definition eigen.hpp:312
deflation_count early_deflation(const schur_target< T > &target, lapack_index ktop, lapack_index kbot, lapack_index nw, std::complex< double > *w, bool recursive)
Aggressive early deflation, as LAPACK's dlaqr3 and zlaqr3: the trailing nw × nw window of the active ...
Definition eigen.hpp:2237
standard_block standardize_block(double &a, double &b, double &c, double &d)
The Schur factorisation of the real 2 × 2 block [a b; c d], as LAPACK's dlanv2: [a b; c d] ← [cs −sn;...
Definition eigen.hpp:668
double real_part(const T &x)
The real part of a real (or general) scalar as a double.
plane_rotation< double > givens(double f, double g)
The rotation that takes (f, g) to (r, 0), as LAPACK's dlartg: r of f's sign.
Definition eigen.hpp:1273
double as_scalar(const std::complex< double > &s, double)
The shift s as the matrix's scalar: a real matrix's are real where they are used one by one.
Definition eigen.hpp:1808
double small_reflector(double(&v)[3], std::size_t count)
The Householder reflection I − τ v vᵀ, v = (1, x…), that takes (α, x…) to (β, 0…),...
Definition eigen.hpp:618
constexpr lapack_index exceptional_after
The iterations without deflation after which the shifts are exceptional (kexsh).
Definition eigen.hpp:1258
void plain_schur(const schur_target< double > &t, std::complex< double > *w)
The Schur form of t's active block by real_schur, its eigenvalues to w (one per row of t).
Definition eigen.hpp:2086
void standardize_at(one_based< double > t, one_based< double > qt, lapack_index n, lapack_index j)
The standard form of the 2 × 2 block at j of the quasi-triangular t, its rotation carried to the rest...
Definition eigen.hpp:1452
void complex_schur(const schur_target< std::complex< double > > &t, buffer< std::complex< double > > &values, std::size_t steps)
The complex Schur form of the upper Hessenberg t's active block, as LAPACK's zlahqr: the shifted QR a...
Definition eigen.hpp:1084
lapack_index recommended_window(lapack_index nh, lapack_index ns)
The deflation window LAPACK's iparmq recommends for an active block of order nh and ns shifts.
Definition eigen.hpp:2404
double modulus_1(double x)
|x| of a real x: what modulus_1 of a complex one stands for.
Definition eigen.hpp:53
double dot_product(const double *x, const double *y, std::size_t count)
Σ x[i] · y[i] for double, as subtract_product chooses its units (in another order of summation there,...
Definition gemm.hpp:369
double largest_part(double x)
|x| of a real x.
Definition eigen.hpp:83
lapack_index undeflated(one_based< double > t, one_based< double > vt, lapack_index jw, double s, double smallest, std::complex< double > *w)
The eigenvalues of the real Schur form t (jw × jw) of a deflation window that do not deflate,...
Definition eigen.hpp:2112
T phase_of(const T &x)
The unit-modulus factor of x: x / |x| (its sign, for a real scalar), and 1 at 0. A subnormal x is sca...
void pair_shifts(std::complex< double > *s, lapack_index count, double)
Arranges a real matrix's shifts so that each pair is a conjugate pair or two reals,...
Definition eigen.hpp:1786
void real_schur(const schur_target< double > &t, buffer< double > &re, buffer< double > &im, std::size_t steps)
The real Schur form of the upper Hessenberg t's active block, as LAPACK's dlahqr: Francis's implicit ...
Definition eigen.hpp:878
scaled_quotient< S > divided(S r, P pivot, double floor)
r / pivot, a pivot below floor taken as floor, r scaled first where the quotient would pass back_subs...
Definition eigen.hpp:2691
matrix< T > adjoint(const matrix< T > &a)
The conjugate transpose of a.
void rotate_pair(T *x, T *y, std::size_t count, std::size_t stride, double c, const T &s)
(x, y) ← (c x + s y, c y − s̄ x) over count pairs stride apart.
Definition eigen.hpp:1305
void solve_upward(const buffer< T > &t, std::size_t n, std::size_t k, std::size_t top, typename X::scalar lambda, double floor, X &x)
Solves (T − λ I) x = 0 upward from the component k, rows k − 1 … 0 of the row-major quasi-triangular ...
Definition eigen.hpp:2853
void rotate(T *x, T *y, std::size_t count, std::size_t stride, double cs, double sn)
(x, y) ← (cs x + sn y, cs y − sn x) over count pairs stride apart: a plane rotation.
Definition eigen.hpp:749
std::complex< double > complex_reflector(std::complex< double >(&v)[2])
The Householder reflection I − τ v vᴴ, v = (1, x), whose adjoint takes (α, x) to (β,...
Definition eigen.hpp:640
constexpr double balance_limit
The largest power of two a balancing scales a row or a column by, either way: the matrix enters at a ...
Definition eigen.hpp:46
buffer< std::complex< double > > complex_vectors(const buffer< std::complex< double > > &t, std::size_t n)
The eigenvectors of the complex upper triangular t (row-major n × n) as the rows of Xᵀ: row k is x wi...
Definition eigen.hpp:2891
void exceptional_shifts(one_based< double > h, std::complex< double > *w, lapack_index ks, lapack_index kbot, lapack_index ktop)
Exceptional shifts for a real matrix, as dlaqr0's after kexsh iterations without deflation: from the ...
Definition eigen.hpp:2412
constexpr lapack_index nibble
The share of the window, in percent, whose deflation makes the sweep unnecessary (nibble).
Definition eigen.hpp:1261
void schur_form(const schur_target< double > &t, buffer< double > &re, buffer< double > &im, std::size_t steps)
The real Schur form of t's active block: real_schur below multishift_from, else multishift_schur,...
Definition eigen.hpp:2612
constexpr std::size_t schur_steps_per_order
The iterations one eigenvalue (or pair) may take per unit of the active order, as dlahqr's 30.
Definition eigen.hpp:41
constexpr lapack_index multishift_from(double)
The order from which a real Schur form is taken by multishift_schur, below which real_schur is faster...
Definition eigen.hpp:1242
balancing balance(buffer< T > &a, std::size_t n)
Balances the row-major n × n a in place, as LAPACK's ‘dgebal('B’)`: rows with no off-diagonal entry i...
Definition eigen.hpp:126
lapack_index recommended_shifts(lapack_index nh)
The shifts LAPACK's iparmq recommends for an active block of order nh.
Definition eigen.hpp:2379
void reflect_rows_3(const std::array< double, 3 > &u, double tau, double *t, std::size_t ld, std::size_t count)
H = I − τ u uᵀ of three entries applied from the left to the 3 × count rows at t (row stride ld).
Definition eigen.hpp:1344
constexpr lapack_index window_growth
The iterations without deflation after which the window grows (kexnw).
Definition eigen.hpp:1255
double scaled_norm(const T *x, std::size_t count)
The Euclidean norm of the count elements at x, by norm_at_scale for this type.
The NumericAL public API.
Definition amx.hpp:44
std::vector< T, detail::default_init_allocator< T > > buffer
The storage of vector, matrix and tensor, namely a std::vector whose buffer<T>(n) and resize(n) leave...
Definition buffer.hpp:157
T sum(const tensor< T > &t)
The sum of every element, by pairwise halving (0 for an empty tensor).
vector< std::complex< double > > eigenvalues(const matrix< T > &a)
The eigenvalues alone of the square a, as numpy.linalg.eigvals.
Definition eigen.hpp:3159
@ amx
The Apple AMX coprocessor, fused multiply-adds; the products it does not suit on the NEON path's tile...
matrix< T > inverse(matrix< T > a)
The inverse of a square matrix.
How a matrix was balanced: swaps, in the order they were applied, then the scaling of the active bloc...
Definition eigen.hpp:97
std::vector< std::pair< std::size_t, std::size_t > > swaps
Rows and columns exchanged, in order.
Definition eigen.hpp:100
std::size_t high
One past its last.
Definition eigen.hpp:99
buffer< double > scale
The diagonal D of D⁻¹ A D (1 outside).
Definition eigen.hpp:101
std::size_t low
The active block's first row and column.
Definition eigen.hpp:98
A complex vector that rows are summed against, kept as (re, −im) and (im, re) pairs: a complex row,...
Definition eigen.hpp:365
std::complex< double > dot(const std::complex< double > *row) const
Σ row[l] v[l].
Definition eigen.hpp:370
buffer< double > real_side
(re, −im) per entry.
Definition eigen.hpp:366
buffer< double > imaginary_side
(im, re) per entry.
Definition eigen.hpp:367
What early_deflation found: the eigenvalues left as shifts, and those deflated.
Definition eigen.hpp:2223
lapack_index shifts
Undeflated: the shifts, at the bottom of the active block.
Definition eigen.hpp:2224
lapack_index deflated
Deflated below them.
Definition eigen.hpp:2225
One panel of hessenberg_blocked, Q_panel = I − V T Vᴴ.
Definition eigen.hpp:408
buffer< T > t
T, row-major width × width, upper triangular.
Definition eigen.hpp:412
std::size_t width
Its columns.
Definition eigen.hpp:410
buffer< T > v
V, row-major (high − first − 1) × width, unit lower trapezoidal.
Definition eigen.hpp:411
std::size_t first
The panel's first column; its reflectors act on rows first + 1 … high − 1.
Definition eigen.hpp:409
A complex matrix's eigenvector during its back-substitution, kept as (re, −im) and (im,...
Definition eigen.hpp:2779
void set(std::size_t j, const std::complex< double > &value)
Sets component j to value.
Definition eigen.hpp:2797
buffer< double > real_side
(re, −im) per component: a row against it gives the product's real part.
Definition eigen.hpp:2782
void scale(std::size_t from, std::size_t to, double factor)
Scales the components from … to by factor.
Definition eigen.hpp:2813
std::complex< double > scalar
Its components.
Definition eigen.hpp:2780
std::complex< double > dot(const std::complex< double > *row, std::size_t from, std::size_t to) const
Σ_{l = from…to} row[l] x[l].
Definition eigen.hpp:2786
std::complex< double > get(std::size_t j) const
Component j.
Definition eigen.hpp:2807
buffer< double > imaginary_side
(im, re) per component: its imaginary part.
Definition eigen.hpp:2783
A row-major matrix read through indices from 1, (1, 1) its first entry.
Definition eigen.hpp:1211
lapack_index ld
The distance between rows.
Definition eigen.hpp:1219
T & operator()(lapack_index i, lapack_index j) const
The entry of row i and column j (from 1).
Definition eigen.hpp:1222
T * data
The first entry.
Definition eigen.hpp:1218
one_based(T *first, lapack_index distance)
The matrix whose first entry is at first, its rows distance apart.
Definition eigen.hpp:1213
A plane rotation [c s; −s̄ c] and what it leaves of its first argument.
Definition eigen.hpp:1266
A real eigenvector during its back-substitution, the sums of rows against it by dot_product.
Definition eigen.hpp:2707
double scalar
Its components.
Definition eigen.hpp:2708
void set(std::size_t j, double value)
Sets component j to value.
Definition eigen.hpp:2721
double dot(const double *row, std::size_t from, std::size_t to) const
Σ_{l = from…to} row[l] x[l].
Definition eigen.hpp:2713
void scale(std::size_t from, std::size_t to, double factor)
Scales the components from … to by factor.
Definition eigen.hpp:2728
buffer< double > x
The components.
Definition eigen.hpp:2710
A real matrix's eigenvectors before their back-transformation: Xᵀ's real and imaginary parts.
Definition eigen.hpp:2911
buffer< double > im
Im Xᵀ, zero where every eigenvalue is real.
Definition eigen.hpp:2913
buffer< double > re
Re Xᵀ, row-major n × n.
Definition eigen.hpp:2912
A real vector that rows are summed against by dot_product.
Definition eigen.hpp:351
double dot(const double *row) const
Σ row[l] v[l].
Definition eigen.hpp:355
buffer< double > v
The vector.
Definition eigen.hpp:352
A quotient of divided and the factor its dividend was scaled by: a type of its own,...
Definition eigen.hpp:2683
double factor
The dividend's scaling, 1 where none was needed.
Definition eigen.hpp:2685
Where a QR sweep applies its reflections: the rows and columns it updates, and Z.
Definition eigen.hpp:858
std::size_t low
The active block's first row.
Definition eigen.hpp:861
std::size_t n
Its order.
Definition eigen.hpp:860
bool full
Whether the whole Schur form is wanted (else the active block alone).
Definition eigen.hpp:863
buffer< T > * h
The Hessenberg matrix, row-major.
Definition eigen.hpp:859
std::size_t high
One past its last.
Definition eigen.hpp:862
buffer< T > * zt
The Schur vectors to accumulate, transposed (row-major Zᵀ), or none.
Definition eigen.hpp:864
A real matrix's complex eigenvector during its back-substitution, its real and imaginary parts apart:...
Definition eigen.hpp:2741
void scale(std::size_t from, std::size_t to, double factor)
Scales the components from … to by factor.
Definition eigen.hpp:2764
buffer< double > im
The imaginary parts.
Definition eigen.hpp:2745
void set(std::size_t j, const std::complex< double > &value)
Sets component j to value.
Definition eigen.hpp:2756
std::complex< double > dot(const double *row, std::size_t from, std::size_t to) const
Σ_{l = from…to} row[l] x[l].
Definition eigen.hpp:2748
buffer< double > re
The real parts.
Definition eigen.hpp:2744
std::complex< double > scalar
Its components.
Definition eigen.hpp:2742
The rotation standardize_block found, and the eigenvalues of the block it standardised.
Definition eigen.hpp:654
double re2
The second's real part.
Definition eigen.hpp:659
double re1
The first eigenvalue's real part.
Definition eigen.hpp:657
double im1
Its imaginary part (positive for a complex pair).
Definition eigen.hpp:658
double im2
Its imaginary part (−im1).
Definition eigen.hpp:660