NumericAL
A header-only C++20 lexer built on REAL
Loading...
Searching...
No Matches
blas.hpp
Go to the documentation of this file.
1
14#ifndef NUMERICAL_BLAS_HPP
15#define NUMERICAL_BLAS_HPP
16
17#include <algorithm>
18#include <array>
19#include <atomic>
20#include <cmath>
21#include <complex>
22#include <cstddef>
23#include <cstdint>
24#include <cstring>
25#include <limits>
26#include <optional>
27#include <utility>
28#include <vector>
29
30#include "decomposition.hpp"
31#include "eigen.hpp"
32#include "gemm.hpp"
33#include "matrix.hpp"
34#include "parallel.hpp"
35
36namespace numerical::blas {
37
40 inline constexpr double lapack_precision {0x1p-53};
41
44 inline constexpr std::size_t syrk_block {128};
45
47 inline constexpr std::size_t syrk_kept_limit {(std::size_t {32} << 20U) / sizeof(double)};
48
49 namespace detail {
50
53 {
54 thread_local buffer<double> storage;
55 return storage;
56 }
57
59 [[nodiscard]] constexpr bool is_operation(char op) noexcept
60 {
61 return op == 'N' || op == 'n' || op == 'T' || op == 't' || op == 'C' || op == 'c';
62 }
63
65 [[nodiscard]] constexpr bool transposes(char op) noexcept
66 {
67 return op != 'N' && op != 'n';
68 }
69
71 [[nodiscard]] constexpr bool conjugates(char op) noexcept
72 {
73 return op == 'C' || op == 'c';
74 }
75
77 template <typename T>
78 [[nodiscard]] bool finite(const T& x)
79 {
80 return std::isfinite(x);
81 }
82
84 template <typename U>
85 [[nodiscard]] bool finite(const std::complex<U>& x)
86 {
87 return std::isfinite(x.real()) && std::isfinite(x.imag());
88 }
89
93 inline constexpr std::size_t transpose_tile {32};
94
99 inline constexpr std::size_t transpose_weight {4};
100
103 template <typename T>
105 const T* a,
106 std::ptrdiff_t lda,
107 std::ptrdiff_t rows,
108 std::ptrdiff_t cols,
109 T scale,
110 bool conjugate = false)
111 {
112 const auto r {static_cast<std::size_t>(rows)};
113 const auto c {static_cast<std::size_t>(cols)};
114 const auto ld {static_cast<std::size_t>(lda)};
115 out.resize(r * c);
116 T* const to {out.data()};
117 numerical::detail::parallel_for((r + transpose_tile - 1) / transpose_tile, transpose_weight * transpose_tile * c, [a, ld, r, c, to, scale, conjugate](std::size_t band) {
118 const std::size_t i0 {band * transpose_tile};
119 for (std::size_t j0 {0}; j0 < c; j0 += transpose_tile) {
120 for (std::size_t j {j0}; j < std::min(j0 + transpose_tile, c); ++j) {
121 for (std::size_t i {i0}; i < std::min(i0 + transpose_tile, r); ++i) {
122 const T x {a[i + (j * ld)]};
123 to[(i * c) + j] = scale * (conjugate ? numerical::detail::conj_of(x) : x);
124 }
125 }
126 }
127 });
128 }
129
132 template <typename T>
134 std::ptrdiff_t lda,
135 std::ptrdiff_t rows,
136 std::ptrdiff_t cols,
137 T scale,
138 bool conjugate = false)
139 {
141 row_major_into(out, a, lda, rows, cols, scale, conjugate);
142 return out;
143 }
144
146 inline constexpr std::size_t kept_bytes {std::size_t {32} << 20U};
147
153 template <typename T>
154 [[nodiscard]] buffer<T>& kept_storage(std::size_t slot) noexcept
155 {
156 thread_local std::array<buffer<T>, 2> kept;
157 return kept[slot];
158 }
159
161 template <typename T>
163 {
164 return std::exchange(kept_storage<T>(slot), buffer<T> {});
165 }
166
168 template <typename T>
169 void give_back(std::size_t slot,
171 {
172 if (storage.capacity() <= kept_bytes / sizeof(T)) {
173 kept_storage<T>(slot) = std::move(storage);
174 }
175 }
176
183 template <typename T>
184 [[nodiscard]] bool is_identity(const T* b,
185 std::size_t ldb,
186 std::size_t n)
187 {
188 buffer<T> unit((2 * n) - 1, T {});
189 unit[n - 1] = T {1};
190 const T* const middle {unit.data() + (n - 1)};
191 if (std::memcmp(b, middle, n * sizeof(T)) != 0) {
192 return false;
193 }
194 std::atomic<bool> holds {true};
195 numerical::detail::parallel_for(n - 1, transpose_weight * n, [b, ldb, n, middle, &holds](std::size_t j) {
196 if (std::memcmp(b + ((j + 1) * ldb), middle - (j + 1), n * sizeof(T)) != 0) {
197 holds.store(false, std::memory_order_relaxed);
198 }
199 });
200 return holds.load(std::memory_order_relaxed);
201 }
202
204 template <typename T>
206 std::size_t rows,
207 std::size_t cols,
208 T* a,
209 std::size_t lda)
210 {
211 numerical::detail::parallel_for((cols + transpose_tile - 1) / transpose_tile, transpose_weight * transpose_tile * rows, [from, rows, cols, a, lda](std::size_t band) {
212 const std::size_t j0 {band * transpose_tile};
213 for (std::size_t i0 {0}; i0 < rows; i0 += transpose_tile) {
214 for (std::size_t i {i0}; i < std::min(i0 + transpose_tile, rows); ++i) {
215 for (std::size_t j {j0}; j < std::min(j0 + transpose_tile, cols); ++j) {
216 a[i + (j * lda)] = from[(i * cols) + j];
217 }
218 }
219 }
220 });
221 }
222
224 template <typename T>
226 std::size_t n,
227 const std::size_t* to,
228 T* a,
229 std::size_t lda)
230 {
232 const std::size_t j0 {band * transpose_tile};
233 for (std::size_t i0 {0}; i0 < n; i0 += transpose_tile) {
234 for (std::size_t i {i0}; i < std::min(i0 + transpose_tile, n); ++i) {
235 for (std::size_t j {j0}; j < std::min(j0 + transpose_tile, n); ++j) {
236 a[i + (to[j] * lda)] = from[(i * n) + j];
237 }
238 }
239 }
240 });
241 }
242
248 template <typename T>
250 std::size_t m,
251 std::size_t n,
252 std::size_t k,
253 const T* l,
254 std::size_t ldl,
255 const T* a,
256 std::size_t lda,
257 T* c,
258 std::size_t ldc)
259 {
260 const bool copied {transposes(transa)};
261 const buffer<T> right {copied ? row_major(a, static_cast<std::ptrdiff_t>(lda), static_cast<std::ptrdiff_t>(k), static_cast<std::ptrdiff_t>(n), T {1}, conjugates(transa))
262 : buffer<T> {}};
263 numerical::detail::gemm(m, n, k, l, ldl, copied ? right.data() : a, copied ? n : lda, c, ldc);
264 }
265
268 inline void product_by_op_a(char transa,
269 std::size_t m,
270 std::size_t n,
271 std::size_t k,
272 const double* l,
273 std::size_t ldl,
274 const double* a,
275 std::size_t lda,
276 double* c,
277 std::size_t ldc)
278 {
279 if (transposes(transa)) {
280 numerical::detail::gemm_bt(m, n, k, l, ldl, a, lda, c, ldc);
281 return;
282 }
283 numerical::detail::gemm(m, n, k, l, ldl, a, lda, c, ldc);
284 }
285
289 template <typename T>
291 std::size_t lda,
292 std::size_t n,
293 T* rows,
294 bool in)
295 {
296 numerical::detail::parallel_for((n + transpose_tile - 1) / transpose_tile, transpose_weight * transpose_tile * n / 2, [a, lda, n, rows, in](std::size_t band) {
297 const std::size_t i0 {band * transpose_tile};
298 for (std::size_t j0 {0}; j0 <= i0; j0 += transpose_tile) {
299 for (std::size_t j {j0}; j < std::min(j0 + transpose_tile, n); ++j) {
300 for (std::size_t i {std::max(i0, j)}; i < std::min(i0 + transpose_tile, n); ++i) {
301 if (in) {
302 rows[(i * n) + j] = a[i + (j * lda)];
303 }
304 else {
305 a[i + (j * lda)] = rows[(i * n) + j];
306 }
307 }
308 }
309 }
310 });
311 }
312
315 template <typename T>
317 std::ptrdiff_t lda,
318 std::ptrdiff_t rows,
319 std::ptrdiff_t cols,
320 T scale)
321 {
322 const auto r {static_cast<std::size_t>(rows)};
323 const auto c {static_cast<std::size_t>(cols)};
324 buffer<T> out(r * c);
325 for (std::size_t j {0}; j < c; ++j) {
326 for (std::size_t i {0}; i < r; ++i) {
327 out[(j * r) + i] = scale * a[i + (j * static_cast<std::size_t>(lda))];
328 }
329 }
330 return out;
331 }
332
339 template <typename T>
341 char transb,
342 T alpha,
343 std::size_t m,
344 std::size_t n,
345 std::size_t k,
346 const T* a,
347 std::size_t lda,
348 const T* b,
349 std::size_t ldb,
350 T* c,
351 std::size_t ldc)
352 {
353 const bool copy_b {transposes(transb) || alpha != T {1}};
354 buffer<T> left {};
355 if (transposes(transb)) {
356 left = row_major(b, static_cast<std::ptrdiff_t>(ldb), static_cast<std::ptrdiff_t>(m), static_cast<std::ptrdiff_t>(k), alpha,
358 }
359 else if (copy_b) {
360 left = columns_as_rows(b, static_cast<std::ptrdiff_t>(ldb), static_cast<std::ptrdiff_t>(k), static_cast<std::ptrdiff_t>(m), alpha);
361 }
362 product_by_op_a(transa, m, n, k, copy_b ? left.data() : b, copy_b ? k : ldb, a, lda, c, ldc);
363 }
364
368 inline void product_of_ops(char transa,
369 char transb,
370 double alpha,
371 std::size_t m,
372 std::size_t n,
373 std::size_t k,
374 const double* a,
375 std::size_t lda,
376 const double* b,
377 std::size_t ldb,
378 double* c,
379 std::size_t ldc)
380 {
381 if (!transposes(transb) || alpha != 1.0) {
382 product_of_ops<double>(transa, transb, alpha, m, n, k, a, lda, b, ldb, c, ldc);
383 return;
384 }
385 if (transposes(transa)) {
386 numerical::detail::gemm_atbt(m, n, k, b, ldb, a, lda, c, ldc);
387 return;
388 }
389 numerical::detail::gemm_at(m, n, k, b, ldb, a, lda, c, ldc);
390 }
391 } // namespace detail
392
393 namespace detail {
394
397 template <typename Index>
398 void interchanges(const std::vector<std::size_t>& pivots,
399 Index* ipiv)
400 {
401 const std::size_t size {pivots.size()};
402 std::vector<std::size_t> at(size); // the original row at each position
403 std::vector<std::size_t> where(size); // the position of each original row
404 for (std::size_t i {0}; i < size; ++i) {
405 at[i] = i;
406 where[i] = i;
407 }
408 for (std::size_t i {0}; i < size; ++i) {
409 const std::size_t from {where[pivots[i]]};
410 ipiv[i] = static_cast<Index>(from + 1);
411 where[at[i]] = from;
412 where[pivots[i]] = i;
413 std::swap(at[i], at[from]);
414 }
415 }
416
421 template <typename T, typename Index>
422 int rectangular_lu(std::size_t m,
423 std::size_t n,
424 T* a,
425 std::size_t lda,
426 Index* ipiv)
427 {
428 int info {0};
429 for (std::size_t j {0}; j < std::min(m, n); ++j) {
430 T* const column {a + (j * lda)};
431 std::size_t pivot {j};
432 for (std::size_t i {j + 1}; i < m; ++i) {
433 if (std::abs(column[i]) > std::abs(column[pivot])) {
434 pivot = i;
435 }
436 }
437 ipiv[j] = static_cast<Index>(pivot + 1);
438 if (column[pivot] == T {}) {
439 info = info == 0 ? static_cast<int>(j + 1) : info;
440 continue;
441 }
442 for (std::size_t c {0}; c < n; ++c) { // the whole rows interchanged, as dgetf2's dswap
443 std::swap(a[j + (c * lda)], a[pivot + (c * lda)]);
444 }
445 for (std::size_t i {j + 1}; i < m; ++i) {
446 column[i] = column[i] / column[j];
447 }
448 for (std::size_t c {j + 1}; c < n; ++c) {
449 T* const target {a + (c * lda)};
450 const T factor {target[j]};
451 for (std::size_t i {j + 1}; i < m; ++i) {
452 target[i] -= column[i] * factor;
453 }
454 }
455 }
456 return info;
457 }
458 } // namespace detail
459
467 template <typename T>
468 int gemm(char transa,
469 char transb,
470 std::ptrdiff_t m,
471 std::ptrdiff_t n,
472 std::ptrdiff_t k,
473 T alpha,
474 const T* a,
475 std::ptrdiff_t lda,
476 const T* b,
477 std::ptrdiff_t ldb,
478 T beta,
479 T* c,
480 std::ptrdiff_t ldc)
481 {
482 const std::ptrdiff_t a_rows {detail::transposes(transa) ? k : m};
483 const std::ptrdiff_t b_rows {detail::transposes(transb) ? n : k};
484 int illegal {0};
486 illegal = 1;
487 }
488 else if (!detail::is_operation(transb)) {
489 illegal = 2;
490 }
491 else if (m < 0) {
492 illegal = 3;
493 }
494 else if (n < 0) {
495 illegal = 4;
496 }
497 else if (k < 0) {
498 illegal = 5;
499 }
500 else if (lda < std::max<std::ptrdiff_t>(1, a_rows)) {
501 illegal = 8;
502 }
503 else if (ldb < std::max<std::ptrdiff_t>(1, b_rows)) {
504 illegal = 10;
505 }
506 else if (ldc < std::max<std::ptrdiff_t>(1, m)) {
507 illegal = 13;
508 }
509 if (illegal != 0 || m == 0 || n == 0) {
510 return illegal;
511 }
512 const auto rows {static_cast<std::size_t>(m)};
513 const auto cols {static_cast<std::size_t>(n)};
514 const auto stride {static_cast<std::size_t>(ldc)};
515 for (std::size_t j {0}; j < cols; ++j) { // C ← β C, column by column
516 T* const column {c + (j * stride)};
517 if (beta == T {}) {
518 std::fill_n(column, rows, T {});
519 }
520 else if (beta != T {1}) {
521 std::transform(column, column + rows, column, [beta](T x) { return beta * x; });
522 }
523 }
524 if (alpha == T {} || k == 0) {
525 return 0;
526 }
527 detail::product_of_ops(transa, transb, alpha, cols, rows, static_cast<std::size_t>(k), a, static_cast<std::size_t>(lda), b,
528 static_cast<std::size_t>(ldb), c, stride);
529 return 0;
530 }
531
532 namespace detail {
533
536 [[nodiscard]] constexpr std::size_t strided(std::size_t i,
537 std::size_t length,
538 std::ptrdiff_t inc) noexcept
539 {
540 return inc > 0 ? i * static_cast<std::size_t>(inc) : (length - 1 - i) * static_cast<std::size_t>(-inc);
541 }
542
544 template <typename T>
545 [[nodiscard]] T real_dot(std::ptrdiff_t n,
546 const T* x,
547 std::ptrdiff_t incx,
548 const T* y,
549 std::ptrdiff_t incy)
550 {
551 if (n <= 0) {
552 return T {};
553 }
554 const auto count {static_cast<std::size_t>(n)};
555 if (incx == 1 && incy == 1) {
556 return numerical::detail::chunked_dot(x, y, count);
557 }
558 buffer<T> xs(count, T {});
559 buffer<T> ys(count, T {});
560 for (std::size_t i {0}; i < count; ++i) {
561 xs[i] = x[strided(i, count, incx)];
562 ys[i] = y[strided(i, count, incy)];
563 }
564 return numerical::detail::chunked_dot(xs.data(), ys.data(), count);
565 }
566 } // namespace detail
567
573 [[nodiscard]] inline double dot(std::ptrdiff_t n,
574 const double* x,
575 std::ptrdiff_t incx,
576 const double* y,
577 std::ptrdiff_t incy)
578 {
579 return detail::real_dot(n, x, incx, y, incy);
580 }
581
583 [[nodiscard]] inline float dot(std::ptrdiff_t n,
584 const float* x,
585 std::ptrdiff_t incx,
586 const float* y,
587 std::ptrdiff_t incy)
588 {
589 return detail::real_dot(n, x, incx, y, incy);
590 }
591
599 template <typename T>
600 [[nodiscard]] std::complex<T> complex_dot(bool conjugate,
601 std::ptrdiff_t n,
602 const std::complex<T>* x,
603 std::ptrdiff_t incx,
604 const std::complex<T>* y,
605 std::ptrdiff_t incy) noexcept
606 {
607 std::complex<T> sum {};
608 if (n <= 0) {
609 return sum;
610 }
611 const auto count {static_cast<std::size_t>(n)};
612 if (incx == 1 && incy == 1) {
614 }
615 for (std::size_t i {0}; i < count; ++i) {
616 const std::complex<T> xi {x[detail::strided(i, count, incx)]};
617 sum += (conjugate ? std::conj(xi) : xi) * y[detail::strided(i, count, incy)];
618 }
619 return sum;
620 }
621
624 inline constexpr std::size_t gemv_rows {64};
625
629 inline constexpr std::size_t gemv_weight {4};
630
638 inline int gemv(char trans,
639 std::ptrdiff_t m,
640 std::ptrdiff_t n,
641 double alpha,
642 const double* a,
643 std::ptrdiff_t lda,
644 const double* x,
645 std::ptrdiff_t incx,
646 double beta,
647 double* y,
648 std::ptrdiff_t incy)
649 {
651 return 1;
652 }
653 if (m < 0) {
654 return 2;
655 }
656 if (n < 0) {
657 return 3;
658 }
659 if (lda < std::max<std::ptrdiff_t>(1, m)) {
660 return 6;
661 }
662 if (incx == 0) {
663 return 8;
664 }
665 if (incy == 0) {
666 return 11;
667 }
668 if (m == 0 || n == 0 || (alpha == 0.0 && beta == 1.0)) {
669 return 0;
670 }
671 const bool transposed {detail::transposes(trans)};
672 const auto rows {static_cast<std::size_t>(m)};
673 const auto cols {static_cast<std::size_t>(n)};
674 const auto ld {static_cast<std::size_t>(lda)};
675 const std::size_t x_length {transposed ? rows : cols};
676 const std::size_t y_length {transposed ? cols : rows};
677 // y itself where its elements are contiguous, else a copy written back; x itself for Aᵀ x where it is contiguous
678 // and α 1, else α x gathered (A x takes −α x, its factors): the same values either way, so the same bits.
680 double* const out {incy == 1 ? y : gathered_y.data()};
681 for (std::size_t i {0}; i < y_length; ++i) {
682 out[i] = beta == 0.0 ? 0.0 : beta * y[detail::strided(i, y_length, incy)];
683 }
684 if (alpha != 0.0) {
685 const bool in_place {transposed && incx == 1 && alpha == 1.0};
686 buffer<double> scaled(in_place ? 0 : x_length); // α x, gathered
687 for (std::size_t i {0}; i < scaled.size(); ++i) {
688 scaled[i] = alpha * x[detail::strided(i, x_length, incx)];
689 }
690 if (transposed) {
691 const double* const factors {in_place ? x : scaled.data()};
692 buffer<double> dots(cols, 0.0);
693 numerical::detail::parallel_for((cols + gemv_rows - 1) / gemv_rows, gemv_weight * gemv_rows * rows, [&](std::size_t group) {
694 const std::size_t first {group * gemv_rows};
695 numerical::detail::row_dots(a + (first * ld), ld, std::min(gemv_rows, cols - first),
696 factors, rows, dots.data() + first);
697 });
698 for (std::size_t j {0}; j < cols; ++j) {
699 out[j] += dots[j];
700 }
701 }
702 else { // A x: each task's rows of y held through every column (numerical::detail::subtract_products, the
703 // factors −α x_j), each element fma(α x_j, a_ij, y_i) for j ascending, as column by column
704 for (double& factor : scaled) {
705 factor = -factor;
706 }
707 numerical::detail::parallel_for((rows + gemv_rows - 1) / gemv_rows, gemv_weight * gemv_rows * cols, [&](std::size_t group) {
708 const std::size_t first {group * gemv_rows};
709 numerical::detail::subtract_products(out + first, scaled.data(), a + first, ld, cols,
710 std::min(gemv_rows, rows - first));
711 });
712 }
713 }
714 for (std::size_t i {0}; incy != 1 && i < y_length; ++i) {
715 y[detail::strided(i, y_length, incy)] = out[i];
716 }
717 return 0;
718 }
719
720 namespace detail {
721
725 {
726 const double* p;
727 std::size_t ldp;
728 const double* q;
729 std::size_t ldq;
730 std::size_t depth;
731 double* c;
732 std::size_t ldc;
733 bool lower;
734 bool q_is_pt;
735 bool p_is_qt;
736 };
737
742 std::size_t c0,
743 std::size_t cols,
744 std::size_t r0,
745 std::size_t rows,
746 double* out,
747 std::size_t ldo)
748 {
749 if (operands.q_is_pt) {
750 numerical::detail::gemm_bt(cols, rows, operands.depth, operands.p + (c0 * operands.ldp), operands.ldp, operands.p + (r0 * operands.ldp),
751 operands.ldp, out, ldo);
752 return;
753 }
754 if (operands.p_is_qt) {
755 numerical::detail::gemm_at(cols, rows, operands.depth, operands.q + c0, operands.ldq, operands.q + r0, operands.ldq, out, ldo);
756 return;
757 }
758 numerical::detail::gemm(cols, rows, operands.depth, operands.p + (c0 * operands.ldp), operands.ldp, operands.q + r0, operands.ldq, out, ldo);
759 }
760
768 std::size_t j0,
769 std::size_t w)
770 {
771 if (w <= syrk_block) {
772 buffer<double> diagonal(w * w, 0.0); // row-major: (i, j) is C(j0 + j, j0 + i)
773 syrk_product(operands, j0, w, j0, w, diagonal.data(), w);
774 for (std::size_t j {0}; j < w; ++j) {
775 for (std::size_t i {operands.lower ? j : 0}; i < (operands.lower ? w : j + 1); ++i) {
776 operands.c[(j0 + i) + ((j0 + j) * operands.ldc)] += diagonal[(j * w) + i];
777 }
778 }
779 return;
780 }
781 const std::size_t h {w / 2};
783 const std::size_t r0 {operands.lower ? j0 + h : j0}; // below the first half (L), or right of it (U)
784 const std::size_t c0 {operands.lower ? j0 : j0 + h};
785 const std::size_t rows {operands.lower ? w - h : h};
786 const std::size_t cols {operands.lower ? h : w - h};
787 syrk_product(operands, c0, cols, r0, rows, operands.c + r0 + (c0 * operands.ldc), operands.ldc);
788 syrk_triangle(operands, j0 + h, w - h);
789 }
790
797 inline void syrk_all(const syrk_operands& operands,
798 std::size_t n)
799 {
800 const std::size_t k {operands.depth};
802 syrk_triangle(operands, 0, n);
803 return;
804 }
805 const bool m_lower {!operands.lower};
806 if (operands.q_is_pt) {
808 n, k, operands.p, operands.ldp, operands.p, operands.ldp, operands.c, operands.ldc, m_lower);
809 return;
810 }
811 if (operands.p_is_qt) {
813 n, k, operands.q, operands.ldq, operands.q, operands.ldq, operands.c, operands.ldc, m_lower);
814 return;
815 }
817 n, k, operands.p, operands.ldp, operands.q, operands.ldq, operands.c, operands.ldc, m_lower);
818 }
819 } // namespace detail
820
828 inline int syrk(char uplo,
829 char trans,
830 std::ptrdiff_t n,
831 std::ptrdiff_t k,
832 double alpha,
833 const double* a,
834 std::ptrdiff_t lda,
835 double beta,
836 double* c,
837 std::ptrdiff_t ldc)
838 {
839 const bool lower {uplo == 'L' || uplo == 'l'};
840 const bool transposed {detail::transposes(trans)};
841 if (!lower && uplo != 'U' && uplo != 'u') {
842 return 1;
843 }
845 return 2;
846 }
847 if (n < 0) {
848 return 3;
849 }
850 if (k < 0) {
851 return 4;
852 }
853 if (lda < std::max<std::ptrdiff_t>(1, transposed ? k : n)) {
854 return 7;
855 }
856 if (ldc < std::max<std::ptrdiff_t>(1, n)) {
857 return 10;
858 }
859 if (n == 0) {
860 return 0;
861 }
862 const auto order {static_cast<std::size_t>(n)};
863 const auto ld {static_cast<std::size_t>(ldc)};
864 if (alpha == 0.0 || k == 0) { // C ← β C on the triangle
865 for (std::size_t j {0}; j < order && beta != 1.0; ++j) {
866 for (std::size_t i {lower ? j : 0}; i < (lower ? order : j + 1); ++i) {
867 c[i + (j * ld)] = beta == 0.0 ? 0.0 : beta * c[i + (j * ld)];
868 }
869 }
870 return 0;
871 }
872 // C(r, c) = α Σ_l op(A)(r, l) op(A)(c, l). The kernel takes row-major operands: P = op(A) (n × k) and
873 // Q = op(A)ᵀ (k × n). A column-major A is one of them read in place (Q for N, P for T). With α = 1 the other is
874 // read from it as its transpose (detail::syrk_product; NumPy's a @ a.T is T): no copy, the same bits. Otherwise
875 // the other is copied once, α folded into it.
876 const auto depth {static_cast<std::size_t>(k)};
877 const auto lda_s {static_cast<std::size_t>(lda)};
878 for (std::size_t j {0}; j < order; ++j) { // C ← β C on the triangle; the products then add into it
879 for (std::size_t i {lower ? j : 0}; i < (lower ? order : j + 1); ++i) {
880 double& entry {c[i + (j * ld)]};
881 entry = beta == 0.0 ? 0.0 : beta * entry;
882 }
883 }
884 if (alpha == 1.0) {
885 detail::syrk_all({transposed ? a : nullptr, lda_s, transposed ? nullptr : a, lda_s, depth, c, ld, lower, transposed, !transposed}, order);
886 return 0;
887 }
888 // The copy in storage the thread keeps between calls (as the kernel's packing_storage): a fresh one of a megabyte
889 // is mapped anew and faulted in page by page on every call.
891 const std::size_t copy_rows {transposed ? depth : order}; // row-major: α A as it lies, k × n (T) or n × k (N)
892 const std::size_t copy_cols {transposed ? order : depth};
893 if (copy.size() < copy_rows * copy_cols) {
895 }
896 for (std::size_t i {0}; i < copy_rows; ++i) {
897 for (std::size_t j {0}; j < copy_cols; ++j) {
898 copy[(i * copy_cols) + j] = alpha * a[i + (j * lda_s)];
899 }
900 }
901 const double* const p_rows {transposed ? a : copy.data()};
902 const std::size_t ldp {transposed ? lda_s : depth};
903 const double* const q_cols {transposed ? copy.data() : a};
904 const std::size_t ldq {transposed ? order : lda_s};
905 detail::syrk_all({p_rows, ldp, q_cols, ldq, depth, c, ld, lower, false, false}, order);
906 if (copy.size() <= syrk_kept_limit) {
907 detail::syrk_operand() = std::move(copy);
908 }
909 return 0;
910 }
911
913 inline constexpr std::size_t trsm_block {64};
914
915 namespace detail {
916
923 inline void triangular_solve(bool lower,
924 bool unit,
925 std::size_t k,
926 std::size_t n,
927 const double* a,
928 std::size_t lda,
929 double* b,
930 std::size_t ldb)
931 {
932 for (std::size_t done {0}; done < k; done += trsm_block) {
933 const std::size_t w {std::min(trsm_block, k - done)};
934 const std::size_t k0 {lower ? done : k - done - w};
935 const std::size_t k1 {k0 + w};
936 // The block's triangle, each column of B alone, the columns in parallel.
937 numerical::detail::parallel_for(n, w * w, [a, b, lda, ldb, k0, k1, lower, unit](std::size_t j) {
938 double* x {b + (j * ldb)};
939 for (std::size_t s {0}; s < k1 - k0; ++s) {
940 const std::size_t p {lower ? k0 + s : k1 - 1 - s};
941 if (!unit) {
942 x[p] /= a[p + (p * lda)];
943 }
944 const std::size_t from {lower ? p + 1 : k0};
945 const std::size_t to {lower ? k1 : p};
946 numerical::detail::add_product(x + from, -x[p], a + from + (p * lda), to - from);
947 }
948 });
949 const std::size_t first {lower ? k1 : 0};
950 const std::size_t count {lower ? k - k1 : k0};
951 if (count > 0) { // B(rows still to solve) −= A(those rows, block) X(block)
952 (void)gemm('N', 'N', static_cast<std::ptrdiff_t>(count), static_cast<std::ptrdiff_t>(n), static_cast<std::ptrdiff_t>(w), -1.0,
953 a + first + (k0 * lda), static_cast<std::ptrdiff_t>(lda), b + k0, static_cast<std::ptrdiff_t>(ldb), 1.0, b + first,
954 static_cast<std::ptrdiff_t>(ldb));
955 }
956 }
957 }
958 } // namespace detail
959
968 inline int trsm(char side,
969 char uplo,
970 char transa,
971 char diag,
972 std::ptrdiff_t m,
973 std::ptrdiff_t n,
974 double alpha,
975 const double* a,
976 std::ptrdiff_t lda,
977 double* b,
978 std::ptrdiff_t ldb)
979 {
980 const bool left {side == 'L' || side == 'l'};
981 const bool lower {uplo == 'L' || uplo == 'l'};
982 const bool unit {diag == 'U' || diag == 'u'};
983 if (!left && side != 'R' && side != 'r') {
984 return 1;
985 }
986 if (!lower && uplo != 'U' && uplo != 'u') {
987 return 2;
988 }
990 return 3;
991 }
992 if (!unit && diag != 'N' && diag != 'n') {
993 return 4;
994 }
995 if (m < 0) {
996 return 5;
997 }
998 if (n < 0) {
999 return 6;
1000 }
1001 if (lda < std::max<std::ptrdiff_t>(1, left ? m : n)) {
1002 return 9;
1003 }
1004 if (ldb < std::max<std::ptrdiff_t>(1, m)) {
1005 return 11;
1006 }
1007 if (m == 0 || n == 0) {
1008 return 0;
1009 }
1010 const auto rows {static_cast<std::size_t>(m)};
1011 const auto cols {static_cast<std::size_t>(n)};
1012 const auto ld {static_cast<std::size_t>(ldb)};
1013 for (std::size_t j {0}; j < cols; ++j) {
1014 for (std::size_t i {0}; i < rows; ++i) {
1015 double& entry {b[i + (j * ld)]};
1016 entry = alpha == 0.0 ? 0.0 : alpha * entry;
1017 }
1018 }
1019 if (alpha == 0.0) {
1020 return 0;
1021 }
1022 const std::size_t k {left ? rows : cols};
1023 const bool flip_a {left == detail::transposes(transa)}; // A' = Aᵀ: op(A) = Aᵀ on the left, A on the right
1025 const double* a_used {a};
1026 std::size_t lda_used {static_cast<std::size_t>(lda)};
1027 if (flip_a) {
1028 transposed_a = buffer<double>(k * k, 0.0);
1029 for (std::size_t j {0}; j < k; ++j) {
1030 for (std::size_t i {lower ? j : 0}; i < (lower ? k : j + 1); ++i) { // A's triangle, transposed
1031 transposed_a[j + (i * k)] = a[i + (j * static_cast<std::size_t>(lda))];
1032 }
1033 }
1034 a_used = transposed_a.data();
1035 lda_used = k;
1036 }
1037 if (left) {
1038 detail::triangular_solve(lower != flip_a, unit, k, cols, a_used, lda_used, b, ld);
1039 return 0;
1040 }
1041 buffer<double> bt(cols * rows, 0.0); // Bᵀ, column-major n × m
1042 for (std::size_t j {0}; j < cols; ++j) {
1043 for (std::size_t i {0}; i < rows; ++i) {
1044 bt[j + (i * cols)] = b[i + (j * ld)];
1045 }
1046 }
1047 detail::triangular_solve(lower != flip_a, unit, k, rows, a_used, lda_used, bt.data(), cols);
1048 for (std::size_t j {0}; j < cols; ++j) {
1049 for (std::size_t i {0}; i < rows; ++i) {
1050 b[i + (j * ld)] = bt[j + (i * cols)];
1051 }
1052 }
1053 return 0;
1054 }
1055
1056 template <typename T, typename Index>
1057 int getrs(char trans,
1058 std::ptrdiff_t n,
1059 std::ptrdiff_t nrhs,
1060 const T* a,
1061 std::ptrdiff_t lda,
1062 const Index* ipiv,
1063 T* b,
1064 std::ptrdiff_t ldb);
1065
1076 template <typename T, typename Index>
1077 int gesv(std::ptrdiff_t n,
1078 std::ptrdiff_t nrhs,
1079 T* a,
1080 std::ptrdiff_t lda,
1081 Index* ipiv,
1082 T* b,
1083 std::ptrdiff_t ldb)
1084 {
1085 if (n < 0) {
1086 return -1;
1087 }
1088 if (nrhs < 0) {
1089 return -2;
1090 }
1091 if (lda < std::max<std::ptrdiff_t>(1, n)) {
1092 return -4;
1093 }
1094 if (ldb < std::max<std::ptrdiff_t>(1, n)) {
1095 return -7;
1096 }
1097 if (n == 0) {
1098 return 0;
1099 }
1100 const auto size {static_cast<std::size_t>(n)};
1101 const auto ld {static_cast<std::size_t>(lda)};
1102 std::vector<std::size_t> pivots;
1103 matrix<T> factors;
1104 bool singular {false};
1105 try { // B and A each transposed in and out once, in storage the thread keeps: the LU solves in B's copy and gives
1106 // its factors up
1107 buffer<T> a_rows {detail::take_kept<T>(0)};
1108 detail::row_major_into(a_rows, a, lda, n, n, T {1});
1109 lu_decomposition<T> lu {matrix<T> {size, size, std::move(a_rows)}};
1110 pivots = lu.pivots();
1111 singular = lu.singular();
1112 if (!singular && nrhs > 0) {
1113 // B = I (NumPy's inv) as the LU's inverse, its forward pass without the identity's zeros: the same terms
1114 // otherwise, so the same bits, where the right-hand sides are too many to go row by row.
1115 const bool inverting {nrhs == n && size > numerical::detail::narrow_solve && detail::is_identity(b, static_cast<std::size_t>(ldb), size)};
1116 buffer<T> b_rows {detail::take_kept<T>(1)};
1117 if (!inverting) {
1118 detail::row_major_into(b_rows, b, ldb, n, nrhs, T {1});
1119 }
1120 // (P A)⁻¹'s columns moved to A⁻¹'s as they are copied out.
1121 matrix<T> x {inverting ? lu.pivoted_inverse(std::move(b_rows)) : lu.solve(matrix<T> {size, static_cast<std::size_t>(nrhs), std::move(b_rows)})};
1122 if (inverting) {
1123 detail::column_major_permuted_into(&*x.begin(), size, lu.pivots().data(), b, static_cast<std::size_t>(ldb));
1124 }
1125 else {
1126 detail::column_major_into(&*x.begin(), size, static_cast<std::size_t>(nrhs), b, static_cast<std::size_t>(ldb));
1127 }
1128 detail::give_back(1, std::move(x).elements());
1129 }
1130 factors = std::move(lu).factors();
1131 }
1132 catch (const non_finite_error&) { // A and B untouched: dgetf2's factors, then getrs, the NaN carried through
1133 const int info {detail::rectangular_lu(size, size, a, ld, ipiv)};
1134 if (info == 0) {
1135 (void)getrs('N', n, nrhs, a, lda, ipiv, b, ldb); // its arguments already checked
1136 }
1137 return info;
1138 }
1139 detail::column_major_into(&*factors.begin(), size, size, a, ld);
1140 detail::give_back(0, std::move(factors).elements());
1141 detail::interchanges(pivots, ipiv);
1142 for (std::size_t i {0}; i < size; ++i) {
1143 if (a[i + (i * ld)] == T {}) { // U(i, i), as just written
1144 return static_cast<int>(i + 1);
1145 }
1146 }
1147 return 0;
1148 }
1149
1150 namespace detail {
1157 template <typename T>
1158 int column_cholesky(bool lower,
1159 std::size_t size,
1160 T* a,
1161 std::size_t ld)
1162 {
1163 // Element (i, j) of L: below the diagonal for lower, the conjugate of U's (j, i) above it otherwise.
1164 const auto l = [&](std::size_t i, std::size_t j) -> T& { return lower ? a[i + (j * ld)] : a[j + (i * ld)]; };
1165 const auto from_l = [&](T x) { return lower ? x : numerical::detail::conj_of(x); }; // L's value as stored
1166 for (std::size_t j {0}; j < size; ++j) {
1167 auto diagonal {std::real(a[j + (j * ld)])};
1168 for (std::size_t k {0}; k < j; ++k) {
1169 const T ljk {from_l(l(j, k))};
1170 diagonal -= std::real(ljk * numerical::detail::conj_of(ljk));
1171 }
1172 if (!(diagonal > 0)) { // not positive, or NaN
1173 a[j + (j * ld)] = T {diagonal};
1174 return static_cast<int>(j + 1);
1175 }
1176 diagonal = std::sqrt(diagonal);
1177 a[j + (j * ld)] = T {diagonal};
1178 for (std::size_t i {j + 1}; i < size; ++i) {
1179 T sum {from_l(l(i, j))};
1180 for (std::size_t k {0}; k < j; ++k) {
1181 sum -= from_l(l(i, k)) * numerical::detail::conj_of(from_l(l(j, k)));
1182 }
1183 l(i, j) = from_l(sum / diagonal);
1184 }
1185 }
1186 return 0;
1187 }
1188 } // namespace detail
1189
1200 template <typename T>
1201 int potrf(char uplo,
1202 std::ptrdiff_t n,
1203 T* a,
1204 std::ptrdiff_t lda)
1205 {
1206 const bool lower {uplo == 'L' || uplo == 'l'};
1207 if (!lower && uplo != 'U' && uplo != 'u') {
1208 return -1;
1209 }
1210 if (n < 0) {
1211 return -2;
1212 }
1213 if (lda < std::max<std::ptrdiff_t>(1, n)) {
1214 return -4;
1215 }
1216 if (n == 0) {
1217 return 0;
1218 }
1219 const auto size {static_cast<std::size_t>(n)};
1220 const auto ld {static_cast<std::size_t>(lda)};
1221 // A's lower triangle, row-major (the upper one's conjugate, Hermitian), in storage the thread keeps: the decomposition
1222 // reads no more of it and zeroes the rest. L's triangle by tiles (a row of it read across columns lda apart missed on
1223 // every element), U's columns as they lie; the factor written back the same ways.
1225 lower_rows.resize(size * size);
1226 if (lower) {
1227 detail::lower_triangle_rows(a, ld, size, lower_rows.data(), true);
1228 }
1229 else {
1230 for (std::size_t i {0}; i < size; ++i) {
1231 for (std::size_t j {0}; j <= i; ++j) {
1232 lower_rows[(i * size) + j] = numerical::detail::conj_of(a[j + (i * ld)]);
1233 }
1234 }
1235 }
1236 try {
1237 buffer<T> l {cholesky_decomposition<T> {matrix<T> {size, size, std::move(lower_rows)}}.lower().elements()};
1238 if (lower) {
1239 detail::lower_triangle_rows(a, ld, size, l.data(), false);
1240 }
1241 else { // U = Lᴴ
1242 for (std::size_t i {0}; i < size; ++i) {
1243 for (std::size_t j {0}; j <= i; ++j) {
1244 a[j + (i * ld)] = numerical::detail::conj_of(l[(i * size) + j]);
1245 }
1246 }
1247 }
1248 detail::give_back(0, std::move(l));
1249 }
1250 catch (const not_positive_definite_error& failed) {
1251 return static_cast<int>(failed.order());
1252 }
1253 catch (const non_finite_error&) { // A untouched: factorised as the reference's dpotf2 does, the NaN carried through
1254 return detail::column_cholesky(lower, size, a, ld);
1255 }
1256 return 0;
1257 }
1258
1266 template <typename T, typename Index>
1267 int getrf(std::ptrdiff_t m,
1268 std::ptrdiff_t n,
1269 T* a,
1270 std::ptrdiff_t lda,
1271 Index* ipiv)
1272 {
1273 if (m < 0) {
1274 return -1;
1275 }
1276 if (n < 0) {
1277 return -2;
1278 }
1279 if (lda < std::max<std::ptrdiff_t>(1, m)) {
1280 return -4;
1281 }
1282 if (m == 0 || n == 0) {
1283 return 0;
1284 }
1285 const auto rows {static_cast<std::size_t>(m)};
1286 const auto cols {static_cast<std::size_t>(n)};
1287 const auto ld {static_cast<std::size_t>(lda)};
1288 if (rows != cols) {
1289 return detail::rectangular_lu(rows, cols, a, ld, ipiv);
1290 }
1291 buffer<T> a_rows {detail::take_kept<T>(0)}; // transposed in and out once, in storage the thread keeps
1292 detail::row_major_into(a_rows, a, lda, m, n, T {1});
1293 std::optional<lu_decomposition<T>> factored; // a square A checked once, by the decomposition, where it was also
1294 try { // checked here, entry by entry across its columns
1295 factored.emplace(matrix<T> {rows, cols, std::move(a_rows)});
1296 }
1297 catch (const non_finite_error&) { // A untouched: factorised as the reference's dgetf2 does, the NaN carried through
1298 return detail::rectangular_lu(rows, cols, a, ld, ipiv);
1299 }
1301 detail::interchanges(lu.pivots(), ipiv);
1302 matrix<T> factors {std::move(lu).factors()};
1303 detail::column_major_into(&*factors.begin(), rows, cols, a, ld);
1304 detail::give_back(0, std::move(factors).elements());
1305 for (std::size_t i {0}; i < rows; ++i) {
1306 if (a[i + (i * ld)] == T {}) { // U(i, i), as just written
1307 return static_cast<int>(i + 1);
1308 }
1309 }
1310 return 0;
1311 }
1312
1319 template <typename T, typename Index>
1320 int getrs(char trans,
1321 std::ptrdiff_t n,
1322 std::ptrdiff_t nrhs,
1323 const T* a,
1324 std::ptrdiff_t lda,
1325 const Index* ipiv,
1326 T* b,
1327 std::ptrdiff_t ldb)
1328 {
1330 return -1;
1331 }
1332 if (n < 0) {
1333 return -2;
1334 }
1335 if (nrhs < 0) {
1336 return -3;
1337 }
1338 if (lda < std::max<std::ptrdiff_t>(1, n)) {
1339 return -5;
1340 }
1341 if (ldb < std::max<std::ptrdiff_t>(1, n)) {
1342 return -8;
1343 }
1344 const auto size {static_cast<std::size_t>(n)};
1345 const auto ld {static_cast<std::size_t>(lda)};
1346 const bool conjugate {detail::conjugates(trans)};
1347 const auto at = [&](std::size_t i, std::size_t j) { return a[i + (j * ld)]; };
1348 // An element of op(A)'s factor read transposed: conjugated for Aᴴ.
1349 const auto across = [&](std::size_t i, std::size_t j) { return conjugate ? numerical::detail::conj_of(at(i, j)) : at(i, j); };
1350 for (std::size_t c {0}; c < static_cast<std::size_t>(nrhs); ++c) {
1351 T* const x {b + (c * static_cast<std::size_t>(ldb))};
1352 if (!detail::transposes(trans)) { // L U x = P b
1353 for (std::size_t i {0}; i < size; ++i) {
1354 std::swap(x[i], x[static_cast<std::size_t>(ipiv[i]) - 1]);
1355 }
1356 for (std::size_t j {0}; j < size; ++j) { // L, unit lower, by columns
1357 for (std::size_t i {j + 1}; i < size; ++i) {
1358 x[i] -= at(i, j) * x[j];
1359 }
1360 }
1361 for (std::size_t j {size}; j-- > 0;) { // U, by columns
1362 x[j] /= at(j, j);
1363 for (std::size_t i {0}; i < j; ++i) {
1364 x[i] -= at(i, j) * x[j];
1365 }
1366 }
1367 continue;
1368 }
1369 for (std::size_t i {0}; i < size; ++i) { // Uᵀ (Uᴴ), by rows of U's columns
1370 T sum {x[i]};
1371 for (std::size_t k {0}; k < i; ++k) {
1372 sum -= across(k, i) * x[k];
1373 }
1374 x[i] = sum / across(i, i);
1375 }
1376 for (std::size_t i {size}; i-- > 0;) { // Lᵀ (Lᴴ)
1377 T sum {x[i]};
1378 for (std::size_t k {i + 1}; k < size; ++k) {
1379 sum -= across(k, i) * x[k];
1380 }
1381 x[i] = sum;
1382 }
1383 for (std::size_t i {size}; i-- > 0;) { // Pᵀ: the interchanges undone in reverse
1384 std::swap(x[i], x[static_cast<std::size_t>(ipiv[i]) - 1]);
1385 }
1386 }
1387 return 0;
1388 }
1389
1396 template <typename T>
1397 int potrs(char uplo,
1398 std::ptrdiff_t n,
1399 std::ptrdiff_t nrhs,
1400 const T* a,
1401 std::ptrdiff_t lda,
1402 T* b,
1403 std::ptrdiff_t ldb)
1404 {
1405 const bool lower {uplo == 'L' || uplo == 'l'};
1406 if (!lower && uplo != 'U' && uplo != 'u') {
1407 return -1;
1408 }
1409 if (n < 0) {
1410 return -2;
1411 }
1412 if (nrhs < 0) {
1413 return -3;
1414 }
1415 if (lda < std::max<std::ptrdiff_t>(1, n)) {
1416 return -5;
1417 }
1418 if (ldb < std::max<std::ptrdiff_t>(1, n)) {
1419 return -7;
1420 }
1421 const auto size {static_cast<std::size_t>(n)};
1422 const auto ld {static_cast<std::size_t>(lda)};
1423 // L(i, j), i ≥ j: U(j, i) conjugated where U = Lᴴ is stored.
1424 const auto l = [&](std::size_t i, std::size_t j) { return lower ? a[i + (j * ld)] : numerical::detail::conj_of(a[j + (i * ld)]); };
1425 for (std::size_t c {0}; c < static_cast<std::size_t>(nrhs); ++c) {
1426 T* const x {b + (c * static_cast<std::size_t>(ldb))};
1427 for (std::size_t i {0}; i < size; ++i) { // L y = b
1428 T sum {x[i]};
1429 for (std::size_t k {0}; k < i; ++k) {
1430 sum -= l(i, k) * x[k];
1431 }
1432 x[i] = sum / l(i, i);
1433 }
1434 for (std::size_t i {size}; i-- > 0;) { // Lᴴ x = y
1435 T sum {x[i]};
1436 for (std::size_t k {i + 1}; k < size; ++k) {
1437 sum -= numerical::detail::conj_of(l(k, i)) * x[k];
1438 }
1439 x[i] = sum / numerical::detail::conj_of(l(i, i));
1440 }
1441 }
1442 return 0;
1443 }
1444
1456 template <typename Index>
1457 int syevd(char jobz,
1458 char uplo,
1459 std::ptrdiff_t n,
1460 double* a,
1461 std::ptrdiff_t lda,
1462 double* w,
1463 double* work,
1464 std::ptrdiff_t lwork,
1465 Index* iwork,
1466 std::ptrdiff_t liwork)
1467 {
1468 const bool vectors {jobz == 'V' || jobz == 'v'};
1469 const bool lower {uplo == 'L' || uplo == 'l'};
1470 if (!vectors && jobz != 'N' && jobz != 'n') {
1471 return -1;
1472 }
1473 if (!lower && uplo != 'U' && uplo != 'u') {
1474 return -2;
1475 }
1476 if (n < 0) {
1477 return -3;
1478 }
1479 if (lda < std::max<std::ptrdiff_t>(1, n)) {
1480 return -5;
1481 }
1482 std::ptrdiff_t lwork_least {1};
1483 std::ptrdiff_t liwork_least {1};
1484 if (n > 1) {
1485 lwork_least = vectors ? 1 + (6 * n) + (2 * n * n) : (2 * n) + 1;
1486 liwork_least = vectors ? 3 + (5 * n) : 1;
1487 }
1488 if (lwork == -1 || liwork == -1) {
1489 work[0] = static_cast<double>(lwork_least);
1490 iwork[0] = static_cast<Index>(liwork_least);
1491 return 0;
1492 }
1493 if (lwork < lwork_least) {
1494 return -8;
1495 }
1496 if (liwork < liwork_least) {
1497 return -10;
1498 }
1499 if (n == 0) {
1500 return 0;
1501 }
1502 const auto size {static_cast<std::size_t>(n)};
1503 const auto ld {static_cast<std::size_t>(lda)};
1504 buffer<double> lower_rows(size * size, 0.0); // the triangle read, as the lower one, row-major
1505 for (std::size_t i {0}; i < size; ++i) {
1506 for (std::size_t j {0}; j <= i; ++j) {
1507 lower_rows[(i * size) + j] = lower ? a[i + (j * ld)] : a[j + (i * ld)];
1508 }
1509 }
1510 try {
1511 const matrix<double> symmetric {size, size, std::move(lower_rows)};
1512 if (!vectors) {
1514 std::copy_n(&values[0], size, w);
1515 return 0;
1516 }
1518 std::copy_n(&eigen.values()[0], size, w);
1519 for (std::size_t j {0}; j < size; ++j) {
1520 for (std::size_t i {0}; i < size; ++i) {
1521 a[i + (j * ld)] = eigen.vectors()(i, j);
1522 }
1523 }
1524 }
1525 catch (const non_finite_error&) { // as the reference's divide and conquer on a NaN: no eigenvalue converges
1526 std::fill_n(w, size, std::numeric_limits<double>::quiet_NaN());
1527 if (vectors) {
1528 for (std::size_t j {0}; j < size; ++j) {
1529 std::fill_n(a + (j * ld), size, std::numeric_limits<double>::quiet_NaN());
1530 }
1531 }
1532 return static_cast<int>(size);
1533 }
1534 return 0;
1535 }
1536
1546 template <typename Index>
1547 int gesdd(char jobz,
1548 std::ptrdiff_t m,
1549 std::ptrdiff_t n,
1550 double* a,
1551 std::ptrdiff_t lda,
1552 double* s,
1553 double* u,
1554 std::ptrdiff_t ldu,
1555 double* vt,
1556 std::ptrdiff_t ldvt,
1557 double* work,
1558 std::ptrdiff_t lwork,
1559 Index* iwork)
1560 {
1561 (void)iwork; // dgesdd's integer workspace, of 8 min(m, n): not used
1562 const bool all {jobz == 'A' || jobz == 'a'};
1563 const bool some {jobz == 'S' || jobz == 's'};
1564 const bool overwrite {jobz == 'O' || jobz == 'o'};
1565 const bool none {jobz == 'N' || jobz == 'n'};
1566 if (!all && !some && !overwrite && !none) {
1567 return -1;
1568 }
1569 if (m < 0) {
1570 return -2;
1571 }
1572 if (n < 0) {
1573 return -3;
1574 }
1575 if (lda < std::max<std::ptrdiff_t>(1, m)) {
1576 return -5;
1577 }
1578 const std::ptrdiff_t least {std::min(m, n)};
1579 const std::ptrdiff_t most {std::max(m, n)};
1580 const bool u_out {all || some || (overwrite && m < n)};
1581 const bool vt_out {all || some || (overwrite && m >= n)};
1582 if (ldu < std::max<std::ptrdiff_t>(1, u_out ? m : 1)) {
1583 return -8;
1584 }
1585 std::ptrdiff_t vt_rows {1};
1586 if (some) {
1587 vt_rows = least;
1588 }
1589 else if (vt_out) { // A, or O with m ≥ n: Vᵀ whole
1590 vt_rows = n;
1591 }
1592 if (ldvt < std::max<std::ptrdiff_t>(1, vt_rows)) {
1593 return -10;
1594 }
1595 std::ptrdiff_t lwork_least {(3 * least) + std::max(most, 7 * least)};
1596 if (overwrite) {
1597 lwork_least = (3 * least) + std::max(most, (5 * least * least) + (4 * least));
1598 }
1599 else if (all || some) {
1600 lwork_least = (3 * least) + std::max(most, (4 * least * least) + (4 * least));
1601 }
1602 if (lwork == -1) {
1603 work[0] = static_cast<double>(std::max<std::ptrdiff_t>(1, lwork_least));
1604 return 0;
1605 }
1606 if (lwork < std::max<std::ptrdiff_t>(1, lwork_least)) {
1607 return -12;
1608 }
1609 if (least == 0) {
1610 return 0;
1611 }
1612 const auto rows {static_cast<std::size_t>(m)};
1613 const auto cols {static_cast<std::size_t>(n)};
1614 const auto k {static_cast<std::size_t>(least)};
1615 try {
1616 const matrix<double> source {rows, cols, detail::row_major(a, lda, m, n, 1.0)};
1617 if (none) {
1618 const vector<double> values {singular_values(source)};
1619 std::copy_n(&values[0], k, s);
1620 return 0;
1621 }
1623 std::copy_n(&svd.singular_values()[0], k, s);
1624 const matrix<double> left {svd.u()};
1625 const matrix<double> right {svd.vh()};
1626 // With O, the factor that fits A's storage goes there, the other to its own argument.
1627 double* const u_into {overwrite && m >= n ? a : u};
1628 const std::size_t ld_u {overwrite && m >= n ? static_cast<std::size_t>(lda) : static_cast<std::size_t>(ldu)};
1629 double* const vt_into {overwrite && m < n ? a : vt};
1630 const std::size_t ld_vt {overwrite && m < n ? static_cast<std::size_t>(lda) : static_cast<std::size_t>(ldvt)};
1631 for (std::size_t j {0}; j < left.cols(); ++j) {
1632 for (std::size_t i {0}; i < left.rows(); ++i) {
1633 u_into[i + (j * ld_u)] = left(i, j);
1634 }
1635 }
1636 for (std::size_t j {0}; j < right.cols(); ++j) {
1637 for (std::size_t i {0}; i < right.rows(); ++i) {
1638 vt_into[i + (j * ld_vt)] = right(i, j);
1639 }
1640 }
1641 }
1642 catch (const non_finite_error&) {
1643 return -4;
1644 }
1645 return 0;
1646 }
1647
1658 inline int geqrf(std::ptrdiff_t m,
1659 std::ptrdiff_t n,
1660 double* a,
1661 std::ptrdiff_t lda,
1662 double* tau,
1663 double* work,
1664 std::ptrdiff_t lwork)
1665 {
1666 if (m < 0) {
1667 return -1;
1668 }
1669 if (n < 0) {
1670 return -2;
1671 }
1672 if (lda < std::max<std::ptrdiff_t>(1, m)) {
1673 return -4;
1674 }
1675 const std::ptrdiff_t lwork_least {std::min(m, n) == 0 ? 1 : n};
1676 if (lwork == -1) {
1677 work[0] = static_cast<double>(lwork_least);
1678 return 0;
1679 }
1680 if (lwork < lwork_least) {
1681 return -7;
1682 }
1683 if (std::min(m, n) == 0) {
1684 return 0;
1685 }
1686 const auto rows {static_cast<std::size_t>(m)};
1687 const auto cols {static_cast<std::size_t>(n)};
1688 const auto ld {static_cast<std::size_t>(lda)};
1689 // In the layout the factorisation runs in (numerical::detail::householder_reflect), in storage the thread keeps: R and
1690 // the reflectors' w₁… where dgeqrf leaves them (numerical::detail::factor_qr_panel, as dlarfg); τ on each block's T
1691 // diagonal, 0 for the identity.
1693#ifdef NUMERICAL_X86_COMPILED
1694 entries.resize(rows * cols); // column-major: copied in and out by columns
1695 for (std::size_t j {0}; j < cols; ++j) {
1696 std::copy_n(a + (j * ld), rows, entries.data() + (j * rows));
1697 }
1698#else
1699 detail::row_major_into(entries, a, lda, m, n, 1.0);
1700#endif
1701#ifdef NUMERICAL_X86_COMPILED
1702 const std::vector<numerical::detail::reflector_block<double>> blocks {numerical::detail::reflect_in_columns(entries, rows, cols)};
1703 for (std::size_t j {0}; j < cols; ++j) {
1704 std::copy_n(entries.data() + (j * rows), rows, a + (j * ld));
1705 }
1706#else
1707 const std::vector<numerical::detail::reflector_block<double>> blocks {numerical::detail::reflect_in_rows(entries, rows, cols)};
1708 detail::column_major_into(entries.data(), rows, cols, a, ld);
1709#endif
1710 detail::give_back(0, std::move(entries));
1712 for (std::size_t j {0}; j < block.width; ++j) {
1713 tau[block.first + j] = block.t[(j * block.width) + j];
1714 }
1715 }
1716 return 0;
1717 }
1718
1728 inline int orgqr(std::ptrdiff_t m,
1729 std::ptrdiff_t n,
1730 std::ptrdiff_t k,
1731 double* a,
1732 std::ptrdiff_t lda,
1733 const double* tau,
1734 double* work,
1735 std::ptrdiff_t lwork)
1736 {
1737 if (m < 0) {
1738 return -1;
1739 }
1740 if (n < 0 || n > m) {
1741 return -2;
1742 }
1743 if (k < 0 || k > n) {
1744 return -3;
1745 }
1746 if (lda < std::max<std::ptrdiff_t>(1, m)) {
1747 return -5;
1748 }
1749 const std::ptrdiff_t lwork_least {std::max<std::ptrdiff_t>(1, n)};
1750 if (lwork == -1) {
1751 work[0] = static_cast<double>(lwork_least);
1752 return 0;
1753 }
1754 if (lwork < lwork_least) {
1755 return -8;
1756 }
1757 if (n == 0) {
1758 return 0;
1759 }
1760 const auto rows {static_cast<std::size_t>(m)};
1761 const auto cols {static_cast<std::size_t>(n)};
1762 const auto depth {static_cast<std::size_t>(k)};
1763 const auto ld {static_cast<std::size_t>(lda)};
1764 // Each reflector w_j = (1, A(j+1…m-1, j)) and τ_j, gathered by blocks into I − V T Vᵀ (T as dlarft forms it).
1765 std::vector<buffer<double>> reflectors(depth);
1766 const std::vector<double> taus(tau, tau + depth);
1767 for (std::size_t j {0}; j < depth; ++j) {
1768 reflectors[j].resize(rows - j);
1769 reflectors[j][0] = 1.0;
1770 std::copy_n(a + (j + 1) + (j * ld), rows - j - 1, reflectors[j].data() + 1);
1771 }
1772 std::vector<numerical::detail::reflector_block<double>> blocks;
1773 for (std::size_t k0 {0}; k0 < depth; k0 += numerical::detail::qr_block) {
1774 blocks.push_back(numerical::detail::gather_reflectors(reflectors, k0, std::min(k0 + numerical::detail::qr_block, depth), rows, &taus));
1775 }
1776 buffer<double> columns {detail::take_kept<double>(0)}; // Q column-major, as numerical::qr() forms it
1777 numerical::detail::form_q_into(columns, blocks, rows, 0, cols, true);
1778 for (std::size_t j {0}; j < cols; ++j) {
1779 std::copy_n(columns.data() + (j * rows), rows, a + (j * ld));
1780 }
1781 detail::give_back(0, std::move(columns));
1782 return 0;
1783 }
1784
1796 template <typename Index>
1797 int gelsd(std::ptrdiff_t m,
1798 std::ptrdiff_t n,
1799 std::ptrdiff_t nrhs,
1800 const double* a,
1801 std::ptrdiff_t lda,
1802 double* b,
1803 std::ptrdiff_t ldb,
1804 double* s,
1805 double rcond,
1806 Index* rank,
1807 double* work,
1808 std::ptrdiff_t lwork,
1809 Index* iwork)
1810 {
1811 if (m < 0) {
1812 return -1;
1813 }
1814 if (n < 0) {
1815 return -2;
1816 }
1817 if (nrhs < 0) {
1818 return -3;
1819 }
1820 if (lda < std::max<std::ptrdiff_t>(1, m)) {
1821 return -5;
1822 }
1823 if (ldb < std::max<std::ptrdiff_t>({1, m, n})) {
1824 return -7;
1825 }
1826 // dgelsd's sizes: its divide and conquer's leaves of 25 values, NLVL levels above them.
1827 constexpr std::ptrdiff_t leaf {25};
1828 const std::ptrdiff_t least {std::max<std::ptrdiff_t>(1, std::min(m, n))};
1829 const auto levels {std::max<std::ptrdiff_t>(
1830 static_cast<std::ptrdiff_t>(std::log2(static_cast<double>(least) / static_cast<double>(leaf + 1))) + 1, 0)};
1831 const std::ptrdiff_t lwork_least {(3 * least) + (9 * least) + (2 * least * leaf) + (8 * least * levels) + (least * nrhs)
1832 + ((leaf + 1) * (leaf + 1))};
1833 if (lwork == -1) {
1834 work[0] = static_cast<double>(lwork_least);
1835 iwork[0] = static_cast<Index>((3 * least * levels) + (11 * least));
1836 return 0;
1837 }
1838 if (lwork < lwork_least) {
1839 return -12;
1840 }
1841 *rank = 0;
1842 if (m == 0 || n == 0) {
1843 return 0;
1844 }
1845 const auto rows {static_cast<std::size_t>(m)};
1846 const auto cols {static_cast<std::size_t>(n)};
1847 const auto rhs {static_cast<std::size_t>(nrhs)};
1848 const auto ld {static_cast<std::size_t>(ldb)};
1849 buffer<double> right(rows * rhs, 0.0);
1850 for (std::size_t i {0}; i < rows; ++i) {
1851 for (std::size_t c {0}; c < rhs; ++c) {
1852 right[(i * rhs) + c] = b[i + (c * ld)];
1853 }
1854 }
1855 if (!std::ranges::all_of(right, [](double x) { return std::isfinite(x); })) {
1856 return -6;
1857 }
1859 try {
1860 const matrix<double> left {rows, cols, detail::row_major(a, lda, m, n, 1.0)};
1861 const least_squares_solution<double> fit {least_squares(left, matrix<double> {rows, rhs, std::move(right)}, cutoff)};
1862 std::copy_n(&fit.singular_values[0], fit.singular_values.size(), s);
1863 *rank = static_cast<Index>(fit.rank);
1864 for (std::size_t c {0}; c < rhs; ++c) {
1865 for (std::size_t i {0}; i < cols; ++i) {
1866 b[i + (c * ld)] = fit.solution(i, c);
1867 }
1868 for (std::size_t i {cols}; i < rows; ++i) {
1869 b[i + (c * ld)] = i == cols && fit.residuals.size() > 0 ? std::sqrt(fit.residuals[c]) : 0.0;
1870 }
1871 }
1872 }
1873 catch (const non_finite_error&) {
1874 return -4;
1875 }
1876 return 0;
1877 }
1878
1879 namespace detail {
1880
1884 inline void real_form(const matrix<std::complex<double>>& v,
1885 const vector<std::complex<double>>& values,
1886 const std::vector<std::size_t>& column_of,
1887 bool conjugate,
1888 double* out,
1889 std::size_t ld)
1890 {
1891 const std::size_t n {values.size()};
1892 const double sign {conjugate ? -1.0 : 1.0};
1893 for (std::size_t j {0}; j < n; ++j) {
1894 const std::size_t c {column_of[j]};
1895 if (values[j].imag() == 0.0) {
1896 for (std::size_t i {0}; i < n; ++i) {
1897 out[i + (j * ld)] = v(i, c).real();
1898 }
1899 }
1900 else if (values[j].imag() > 0.0) {
1901 for (std::size_t i {0}; i < n; ++i) {
1902 out[i + (j * ld)] = v(i, c).real();
1903 out[i + ((j + 1) * ld)] = sign * v(i, c).imag();
1904 }
1905 }
1906 }
1907 }
1908 } // namespace detail
1909
1922 inline int geev(char jobvl,
1923 char jobvr,
1924 std::ptrdiff_t n,
1925 const double* a,
1926 std::ptrdiff_t lda,
1927 double* wr,
1928 double* wi,
1929 double* vl,
1930 std::ptrdiff_t ldvl,
1931 double* vr,
1932 std::ptrdiff_t ldvr,
1933 double* work,
1934 std::ptrdiff_t lwork)
1935 {
1936 const bool left {jobvl == 'V' || jobvl == 'v'};
1937 const bool right {jobvr == 'V' || jobvr == 'v'};
1938 if (!left && jobvl != 'N' && jobvl != 'n') {
1939 return -1;
1940 }
1941 if (!right && jobvr != 'N' && jobvr != 'n') {
1942 return -2;
1943 }
1944 if (n < 0) {
1945 return -3;
1946 }
1947 if (lda < std::max<std::ptrdiff_t>(1, n)) {
1948 return -5;
1949 }
1950 if (ldvl < 1 || (left && ldvl < n)) {
1951 return -9;
1952 }
1953 if (ldvr < 1 || (right && ldvr < n)) {
1954 return -11;
1955 }
1956 const std::ptrdiff_t lwork_least {std::max<std::ptrdiff_t>(1, (left || right ? 4 : 3) * n)};
1957 if (lwork == -1) {
1958 work[0] = static_cast<double>(lwork_least);
1959 return 0;
1960 }
1961 if (lwork < lwork_least) {
1962 return -13;
1963 }
1964 if (n == 0) {
1965 return 0;
1966 }
1967 const auto size {static_cast<std::size_t>(n)};
1968 try {
1969 const matrix<double> source {size, size, detail::row_major(a, lda, n, n, 1.0)};
1971 const vector<std::complex<double>>& values {eigen.values()};
1972 for (std::size_t j {0}; j < size; ++j) {
1973 wr[j] = values[j].real();
1974 wi[j] = values[j].imag();
1975 }
1976 std::vector<std::size_t> same(size);
1977 for (std::size_t j {0}; j < size; ++j) {
1978 same[j] = j;
1979 }
1980 if (right) {
1981 detail::real_form(eigen.vectors(), values, same, false, vr, static_cast<std::size_t>(ldvr));
1982 }
1983 if (left) {
1984 const eigen_decomposition<double> transposed {matrix<double> {size, size, detail::columns_as_rows(a, lda, n, n, 1.0)}, true};
1985 // Aᵀ w = λ w is (conj w)ᴴ A = λ (conj w)ᴴ: each eigenvalue's left vector is the conjugate of Aᵀ's right one
1986 // for the same eigenvalue, the nearest one not yet taken (a multiple eigenvalue takes its vectors in turn).
1987 std::vector<std::size_t> column_of(size);
1988 std::vector<bool> taken(size, false);
1989 for (std::size_t j {0}; j < size; ++j) {
1990 std::size_t best {size};
1991 for (std::size_t p {0}; p < size; ++p) {
1992 if (!taken[p] && (best == size || std::abs(transposed.values()[p] - values[j]) < std::abs(transposed.values()[best] - values[j]))) {
1993 best = p;
1994 }
1995 }
1996 taken[best] = true;
1997 column_of[j] = best;
1998 }
1999 detail::real_form(transposed.vectors(), values, column_of, true, vl, static_cast<std::size_t>(ldvl));
2000 }
2001 }
2002 catch (const non_finite_error&) {
2003 return -4;
2004 }
2005 return 0;
2006 }
2007} // namespace numerical::blas
2008
2009#endif // NUMERICAL_BLAS_HPP
A Cholesky factorisation A = L Lᴴ of a Hermitian positive-definite matrix.
The eigenvalues, and optionally the right eigenvectors, of a general square matrix,...
Definition eigen.hpp:2980
An LU factorisation with partial pivoting of a square matrix.
A dense, row-major matrix of T with value semantics.
Definition matrix.hpp:35
buffer< T >::const_iterator begin() const noexcept
Const iterator to the first element (row-major).
Definition matrix.hpp:132
buffer< T > elements() &&
The elements, row-major, handed over by a matrix about to expire, which is left 0 × 0: a large result...
Definition matrix.hpp:145
Thrown when a decomposition's input holds an infinite or NaN entry: no factorisation of it means anyt...
Thrown when a Cholesky factorisation finds the matrix not positive-definite.
std::size_t order() const noexcept
The order of the first leading minor found not positive definite (LAPACK's info), 0 where unknown.
The magnitude at or under which a singular value counts as zero, in a rank (matrix_rank),...
static rank_cutoff relative(double ratio)
The largest singular value times ratio.
The singular value decomposition A = U Σ Vᴴ of an m × n matrix.
The eigenvalues and eigenvectors of a real symmetric matrix.
A dense vector of T with value semantics.
Definition vector.hpp:86
Matrix factorisations and the algorithms built on them.
The eigenvalues and eigenvectors of a general square matrix.
The matrix product kernel behind matrix * matrix and the einsum matmul path.
A dense, row-major, value-semantic matrix over a scalar type.
void syrk_product(const syrk_operands &operands, std::size_t c0, std::size_t cols, std::size_t r0, std::size_t rows, double *out, std::size_t ldo)
P(c0…) Q(…, r0…) added to the cols × rows row-major block at out (ldo apart): numerical::detail::gemm...
Definition blas.hpp:741
void syrk_triangle(syrk_operands operands, std::size_t j0, std::size_t w)
Adds P Q to the triangle of the diagonal block [j0, j0 + w) of C: halved until it is a block of syrk_...
Definition blas.hpp:767
buffer< double > & syrk_operand() noexcept
The storage of syrk's operand copy this thread keeps between calls.
Definition blas.hpp:52
void lower_triangle_rows(T *a, std::size_t lda, std::size_t n, T *rows, bool in)
The lower triangle of the column-major n × n a (lda apart) into that of the row-major rows (n apart) ...
Definition blas.hpp:290
buffer< T > & kept_storage(std::size_t slot) noexcept
Row-major storage of the scalar T this thread keeps between calls, in slot (0 for a call's A,...
Definition blas.hpp:154
bool is_identity(const T *b, std::size_t ldb, std::size_t n)
Whether the column-major n × n b (ldb apart) is the identity to the bit (a -0 is no zero of it,...
Definition blas.hpp:184
void triangular_solve(bool lower, bool unit, std::size_t k, std::size_t n, const double *a, std::size_t lda, double *b, std::size_t ldb)
A X = B in place for the column-major triangular k × k a (lower or upper, a unit diagonal not read) a...
Definition blas.hpp:923
constexpr std::size_t strided(std::size_t i, std::size_t length, std::ptrdiff_t inc) noexcept
Index of element i of a BLAS vector of length elements inc apart: from the last one where inc is nega...
Definition blas.hpp:536
bool finite(const T &x)
Whether the real x is finite (an overload per kind of scalar, as conj_of: each one expression).
Definition blas.hpp:78
void column_major_permuted_into(const T *from, std::size_t n, const std::size_t *to, T *a, std::size_t lda)
column_major_into for the n × n from, its column j written as column to[j] of a.
Definition blas.hpp:225
constexpr bool is_operation(char op) noexcept
Whether op is one of BLAS's N, T or C (either case): no transpose, transpose, conjugate one.
Definition blas.hpp:59
constexpr std::size_t kept_bytes
The most bytes a thread keeps in each of kept_storage's slots between calls (32 MiB).
Definition blas.hpp:146
int rectangular_lu(std::size_t m, std::size_t n, T *a, std::size_t lda, Index *ipiv)
The LU factorisation with partial pivoting of a rectangular column-major A in place,...
Definition blas.hpp:422
void give_back(std::size_t slot, buffer< T > &&storage)
storage given back to kept_storage's slot, unless past kept_bytes.
Definition blas.hpp:169
void interchanges(const std::vector< std::size_t > &pivots, Index *ipiv)
The permutation pivots (row i of P A is row pivots[i] of A) as LAPACK's successive row interchanges,...
Definition blas.hpp:398
constexpr bool conjugates(char op) noexcept
Whether op is BLAS's C: the conjugate transpose (a real matrix's is its transpose).
Definition blas.hpp:71
void row_major_into(buffer< T > &out, const T *a, std::ptrdiff_t lda, std::ptrdiff_t rows, std::ptrdiff_t cols, T scale, bool conjugate=false)
The column-major rows × cols a (lda apart) into the row-major out (resized to it),...
Definition blas.hpp:104
void column_major_into(const T *from, std::size_t rows, std::size_t cols, T *a, std::size_t lda)
The row-major rows × cols from into the column-major a (lda apart), by tiles.
Definition blas.hpp:205
buffer< T > row_major(const T *a, std::ptrdiff_t lda, std::ptrdiff_t rows, std::ptrdiff_t cols, T scale, bool conjugate=false)
rows × cols of the column-major a (lda apart), as a row-major buffer, each element times scale (conju...
Definition blas.hpp:133
buffer< T > take_kept(std::size_t slot)
kept_storage's slot, taken over by the caller (left empty there meanwhile).
Definition blas.hpp:162
void product_by_op_a(char transa, std::size_t m, std::size_t n, std::size_t k, const T *l, std::size_t ldl, const T *a, std::size_t lda, T *c, std::size_t ldc)
C += L op(A)ᵀ (C row-major m × n, L m × k), a the column-major op(A)'s storage (lda apart): op(A)ᵀ is...
Definition blas.hpp:249
int column_cholesky(bool lower, std::size_t size, T *a, std::size_t ld)
The Cholesky factor of the column-major order-size A in place, column by column as the reference's dp...
Definition blas.hpp:1158
void syrk_all(const syrk_operands &operands, std::size_t n)
The whole triangle of C (order n) from operands: a product the matrix product kernel packs runs in on...
Definition blas.hpp:797
buffer< T > columns_as_rows(const T *a, std::ptrdiff_t lda, std::ptrdiff_t rows, std::ptrdiff_t cols, T scale)
The cols columns of rows elements of the column-major a (lda apart) as the rows of a row-major buffer...
Definition blas.hpp:316
void real_form(const matrix< std::complex< double > > &v, const vector< std::complex< double > > &values, const std::vector< std::size_t > &column_of, bool conjugate, double *out, std::size_t ld)
The eigenvectors v (columns, complex) in dgeev's real form, into the column-major out: column j of a ...
Definition blas.hpp:1884
constexpr bool transposes(char op) noexcept
Whether op transposes (T or C: a real matrix's conjugate transpose is its transpose).
Definition blas.hpp:65
constexpr std::size_t transpose_weight
The weight of an element of the transposing copies against a multiply-add: bound by the memory,...
Definition blas.hpp:99
T real_dot(std::ptrdiff_t n, const T *x, std::ptrdiff_t incx, const T *y, std::ptrdiff_t incy)
numerical::blas::dot of the real scalar T.
Definition blas.hpp:545
constexpr std::size_t transpose_tile
The tile side of the transposing copies between column-major arguments and row-major storage: a tile'...
Definition blas.hpp:93
void product_of_ops(char transa, char transb, T alpha, 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 += α op(B)ᵀ op(A)ᵀ, row-major C (m × n, ldc apart), the column-major C of BLAS's gemm as its transp...
Definition blas.hpp:340
std::complex< T > complex_dot(bool conjugate, std::ptrdiff_t n, const std::complex< T > *x, std::ptrdiff_t incx, const std::complex< T > *y, std::ptrdiff_t incy) noexcept
Σ x_i y_i (conjugate false, BLAS's zdotu) or Σ conj(x_i) y_i (true, zdotc) over n complex elements in...
Definition blas.hpp:600
int potrf(char uplo, std::ptrdiff_t n, T *a, std::ptrdiff_t lda)
The Cholesky factor of a symmetric positive-definite A, column-major, as LAPACK's dpotrf: with uplo L...
Definition blas.hpp:1201
int gemm(char transa, char transb, std::ptrdiff_t m, std::ptrdiff_t n, std::ptrdiff_t k, T alpha, const T *a, std::ptrdiff_t lda, const T *b, std::ptrdiff_t ldb, T beta, T *c, std::ptrdiff_t ldc)
C ← α op(A) op(B) + β C, column-major, as BLAS's dgemm: op(A) is m × k, op(B) k × n,...
Definition blas.hpp:468
double dot(std::ptrdiff_t n, const double *x, std::ptrdiff_t incx, const double *y, std::ptrdiff_t incy)
Σ x_i y_i over n elements incx and incy apart (from the last where negative), as BLAS's ddot; 0 where...
Definition blas.hpp:573
int getrf(std::ptrdiff_t m, std::ptrdiff_t n, T *a, std::ptrdiff_t lda, Index *ipiv)
P A = L U of the column-major m × n A in place, as LAPACK's dgetrf: the factors as dgetrf leaves them...
Definition blas.hpp:1267
int trsm(char side, char uplo, char transa, char diag, std::ptrdiff_t m, std::ptrdiff_t n, double alpha, const double *a, std::ptrdiff_t lda, double *b, std::ptrdiff_t ldb)
B ← α op(A)⁻¹ B (side L) or α B op(A)⁻¹ (R), column-major, B m × n and the triangular A (uplo U or L,...
Definition blas.hpp:968
constexpr std::size_t syrk_block
The columns of syrk's smallest diagonal blocks, formed whole: their wasted half, at most 64 n k multi...
Definition blas.hpp:44
int orgqr(std::ptrdiff_t m, std::ptrdiff_t n, std::ptrdiff_t k, double *a, std::ptrdiff_t lda, const double *tau, double *work, std::ptrdiff_t lwork)
The first n columns of Q = H₀ H₁ … H_{k-1}, from the reflectors of a dgeqrf (geqrf's or any implement...
Definition blas.hpp:1728
constexpr double lapack_precision
LAPACK's relative machine precision, ‘dlamch('E’): the unit roundoff2⁻⁵³, half of std::numeric_limits...
Definition blas.hpp:40
constexpr std::size_t trsm_block
The rows of trsm's blocks: within one, each column solved alone; past it, the matrix product kernel.
Definition blas.hpp:913
int syevd(char jobz, char uplo, std::ptrdiff_t n, double *a, std::ptrdiff_t lda, double *w, double *work, std::ptrdiff_t lwork, Index *iwork, std::ptrdiff_t liwork)
The eigenvalues, and with jobz V the eigenvectors, of a symmetric column-major A, as LAPACK's dsyevd:...
Definition blas.hpp:1457
int gesdd(char jobz, std::ptrdiff_t m, std::ptrdiff_t n, double *a, std::ptrdiff_t lda, double *s, double *u, std::ptrdiff_t ldu, double *vt, std::ptrdiff_t ldvt, double *work, std::ptrdiff_t lwork, Index *iwork)
The singular value decomposition A = U Σ Vᵀ of a column-major m × n A, as LAPACK's dgesdd: the singul...
Definition blas.hpp:1547
constexpr std::size_t syrk_kept_limit
The most elements of syrk's operand copy a thread keeps between calls (32 MiB).
Definition blas.hpp:47
int gemv(char trans, std::ptrdiff_t m, std::ptrdiff_t n, double alpha, const double *a, std::ptrdiff_t lda, const double *x, std::ptrdiff_t incx, double beta, double *y, std::ptrdiff_t incy)
y ← α op(A) x + β y, column-major A m × n, as BLAS's dgemv: the vectors incx and incy apart (from the...
Definition blas.hpp:638
int geev(char jobvl, char jobvr, std::ptrdiff_t n, const double *a, std::ptrdiff_t lda, double *wr, double *wi, double *vl, std::ptrdiff_t ldvl, double *vr, std::ptrdiff_t ldvr, double *work, std::ptrdiff_t lwork)
The eigenvalues, and with jobvl or jobvr V the left or right eigenvectors, of a column-major n × n A,...
Definition blas.hpp:1922
int potrs(char uplo, std::ptrdiff_t n, std::ptrdiff_t nrhs, const T *a, std::ptrdiff_t lda, T *b, std::ptrdiff_t ldb)
A X = B from the Cholesky factor of a dpotrf (by any implementation), column-major,...
Definition blas.hpp:1397
int getrs(char trans, std::ptrdiff_t n, std::ptrdiff_t nrhs, const T *a, std::ptrdiff_t lda, const Index *ipiv, T *b, std::ptrdiff_t ldb)
A X = B, Aᵀ X = B or Aᴴ X = B (trans N, T or C), column-major, from the factors and interchanges of a...
Definition blas.hpp:1320
int syrk(char uplo, char trans, std::ptrdiff_t n, std::ptrdiff_t k, double alpha, const double *a, std::ptrdiff_t lda, double beta, double *c, std::ptrdiff_t ldc)
C ← α op(A) op(A)ᵀ + β C on the triangle uplo (U or L) of the column-major n × n C,...
Definition blas.hpp:828
int gesv(std::ptrdiff_t n, std::ptrdiff_t nrhs, T *a, std::ptrdiff_t lda, Index *ipiv, T *b, std::ptrdiff_t ldb)
A X = B, column-major, as LAPACK's dgesv: A (n × n, lda apart) is factorised as P A = L U by NumericA...
Definition blas.hpp:1077
int gelsd(std::ptrdiff_t m, std::ptrdiff_t n, std::ptrdiff_t nrhs, const double *a, std::ptrdiff_t lda, double *b, std::ptrdiff_t ldb, double *s, double rcond, Index *rank, double *work, std::ptrdiff_t lwork, Index *iwork)
The minimum-norm least-squares solutions of A X ≈ B, column-major A m × n and B max(m,...
Definition blas.hpp:1797
constexpr std::size_t gemv_rows
The rows (or, for Aᵀ x, the columns) each task of gemv takes, 64 so that an order of a few hundred sh...
Definition blas.hpp:624
int geqrf(std::ptrdiff_t m, std::ptrdiff_t n, double *a, std::ptrdiff_t lda, double *tau, double *work, std::ptrdiff_t lwork)
The QR factorisation A = Q R of a column-major m × n A, as LAPACK's dgeqrf: R overwrites the upper tr...
Definition blas.hpp:1658
constexpr std::size_t gemv_weight
The weight of gemv's multiply-adds against a compute-bound product's: each reads an element of A once...
Definition blas.hpp:629
constexpr std::size_t gemm_packed_threshold
Below this many multiply-adds the plain loop (gemm_rows, by blocks of rows held in registers) runs in...
Definition gemm.hpp:513
C complex_dot_product(const C *x, const C *y, std::size_t count)
Σ x[i] · y[i] of complex numbers: on the x86-64 units with AVX2 (the AVX-512 path too) their fused ch...
Definition gemm.hpp:389
void form_q_into(buffer< T > &out, const std::vector< reflector_block< T > > &blocks, std::size_t rows, std::size_t first, std::size_t count, bool transposed)
The columns [first, first + count) of Q = H₀ H₁ …, the blocks applied (the last first) to those of th...
T conjugate_dot_product(const T *x, const T *y, std::size_t count)
Σ conj(x[i]) · y[i], in order, where no native vector holds the parts.
Definition simd.hpp:739
T chunked_dot(const T *a, const T *b, std::size_t count)
Σ a[i] · b[i] for a plain dot product: chunks of dot_chunk elements in parallel, each by dot_product ...
Definition gemm.hpp:3142
void gemm_at(std::size_t m, std::size_t n, std::size_t k, const T *at, std::size_t ldat, const T *b, std::size_t ldb, T *c, std::size_t ldc)
gemm_layouts with A given as Aᵀ (at, k × m, ldat apart).
Definition gemm.hpp:2877
void add_product(double *y, const double &f, const double *x, std::size_t count)
y[i] += f · x[i] for double, as subtract_product.
Definition gemm.hpp:308
void row_dots(const std::complex< double > *m, std::size_t ld, std::size_t rows, const std::complex< double > *x, std::size_t count, std::complex< double > *out)
row_dots for std::complex<double>, through complex_row_dots.
Definition gemm.hpp:432
void subtract_products(double *y, const double *f, const double *x, std::size_t ldx, std::size_t terms, std::size_t count)
y[i] -= Σₜ f[t] · x[(t · ldx) + i] for double, the units chosen as subtract_product chooses them,...
Definition gemm.hpp:194
std::vector< reflector_block< T > > reflect_in_rows(buffer< T > &a, std::size_t rows, std::size_t cols, buffer< T > *r=nullptr)
The blocks of reflectors of a Householder QR of the row-major a (rows × cols), factorised in place as...
T conj_of(const T &x)
The conjugate of a real (or general) scalar — the value itself.
void gemm_bt(std::size_t m, std::size_t n, std::size_t k, const T *a, std::size_t lda, const T *bt, std::size_t ldbt, T *c, std::size_t ldc)
gemm_layouts with B given as Bᵀ (bt, n × k, ldbt apart).
Definition gemm.hpp:2862
constexpr std::size_t narrow_solve
The most right-hand sides an LU solve takes row by row rather than by blocks through the product kern...
reflector_block< T > gather_reflectors(const std::vector< buffer< T > > &reflectors, std::size_t k0, std::size_t k1, std::size_t rows, const std::vector< T > *taus=nullptr)
Gathers the reflectors reflectors[k0…k1-1] (each I − τ_k v vᴴ: τ_k from taus, else 2 for a unit v; an...
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
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
constexpr std::size_t qr_block
The reflectors of one panel of the blocked QR.
constexpr std::size_t gemm_min_depth
Below this depth the plain loop runs too: each panel reads and writes its tiles of C once,...
Definition gemm.hpp:519
std::vector< reflector_block< T > > reflect_in_columns(buffer< T > &columns, std::size_t rows, std::size_t cols)
The blocks of reflectors of a Householder QR of the column-major columns (rows per column,...
void gemm_atbt(std::size_t m, std::size_t n, std::size_t k, const T *at, std::size_t ldat, const T *bt, std::size_t ldbt, T *c, std::size_t ldc)
gemm_layouts with both operands given as their transposes.
Definition gemm.hpp:2892
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).
least_squares_solution< T > least_squares(const matrix< T > &a, const matrix< T > &b, rank_cutoff cutoff={})
lstsq, with the residuals, the rank and the singular values numpy.linalg.lstsq returns beside the sol...
@ amx
The Apple AMX coprocessor, fused multiply-adds; the products it does not suit on the NEON path's tile...
@ divide_and_conquer
Golub–Kahan bidiagonalisation then divide and conquer, as LAPACK's dgesdd: the default.
vector< double > singular_values(const matrix< T > &a)
The singular values of a, descending, without its singular vectors, as numpy.linalg....
vector< double > symmetric_eigenvalues(const matrix< T > &a)
The eigenvalues of the real symmetric or complex Hermitian a, ascending, without its eigenvectors,...
A minimal std::thread parallel-for for the hot kernels.
The operands of syrk_triangle, which are row-major P = op(A) and Q = op(A)ᵀ (one of them scaled by α)...
Definition blas.hpp:725
std::size_t ldp
P's row distance.
Definition blas.hpp:727
bool q_is_pt
Q is Pᵀ read from P's rows (numerical::detail::gemm_bt), q unused.
Definition blas.hpp:734
const double * p
P, n × depth.
Definition blas.hpp:726
bool p_is_qt
P is Qᵀ read from Q's columns (numerical::detail::gemm_at), p unused.
Definition blas.hpp:735
const double * q
Q, depth × n.
Definition blas.hpp:728
std::size_t ldc
C's column distance.
Definition blas.hpp:732
std::size_t ldq
Q's row distance.
Definition blas.hpp:729
bool lower
The lower triangle (else the upper).
Definition blas.hpp:733
double * c
C, column-major.
Definition blas.hpp:731
The reflectors of one panel of the blocked QR, I − V T Vᴴ as LAPACK's dlarft forms it.
A least-squares solution and what numpy.linalg.lstsq reports beside it (least_squares).