22#ifndef NUMERICAL_DECOMPOSITION_HPP
23#define NUMERICAL_DECOMPOSITION_HPP
69 return std::complex<R> {1} /
pivot;
100 const std::complex<R>& a,
101 const std::complex<R>& b)
110#ifdef NUMERICAL_X86_COMPILED
129 static_assert(std::has_single_bit(
lu_small_block) && std::has_single_bit(
lu_block),
"a panel is a mask away");
141#ifdef NUMERICAL_X86_COMPILED
150#ifdef NUMERICAL_X86_COMPILED
162#ifdef NUMERICAL_X86_COMPILED
186#ifdef NUMERICAL_X86_COMPILED
213#ifdef NUMERICAL_X86_COMPILED
253 template <
typename T>
260 template <
typename U>
267 template <
typename T>
272 template <
typename T>
279 template <
typename T>
282 return static_cast<double>(x);
286 template <
typename U>
289 return static_cast<double>(x.real());
294 template <
typename T>
296 std::conditional_t<std::is_same_v<T, float> || std::is_same_v<T, std::complex<float>>,
float,
double>;
300 template <
typename T>
301 inline constexpr double rescue_below {std::is_same_v<value_range_t<T>,
float> ? 0x1p-60 : 0x1p-500};
307 template <
typename T>
310 constexpr double least {
static_cast<double>(std::numeric_limits<value_range_t<T>>
::min())};
319 template <
typename T>
323 const double magnitude {
static_cast<double>(std::abs(scaled))};
333 template <
typename T>
349 template <
typename T>
354 for (std::size_t i {0}; i < count; ++i) {
361 for (std::size_t i {0}; i < count; ++i) {
362 const double ratio {
static_cast<double>(std::abs(x[i])) /
largest};
377 template <
typename T>
383 return std::sqrt(
plain);
389 template <
typename T>
408 const auto*
const parts {
reinterpret_cast<const double*
>(x)};
413 template <
typename T>
429 constexpr double low {0x1p-500};
430 constexpr double high {0x1p500};
431 const double larger {std::max(std::abs(a), std::abs(b))};
433 return std::sqrt((a * a) + (b * b));
435 return std::hypot(a, b);
465 template <
typename T>
468 return std::isfinite(x);
472 template <
typename U>
475 return std::isfinite(x.real()) && std::isfinite(x.imag());
482 template <
typename T>
486 for (std::size_t i {0}; i < a.
rows(); ++i) {
487 for (std::size_t j {0}; j < a.
cols(); ++j) {
489 throw non_finite_error(std::string {what} +
": the matrix holds an infinite or NaN entry");
506 std::size_t count)
noexcept
508 constexpr std::uint64_t exponent {0x7FF0000000000000ULL};
509 constexpr std::uint64_t
carry {std::uint64_t {1} << 52U};
520 for (; i + 8 <= count; i += 8) {
521 std::array<pair, 4>
bits {};
522 std::memcpy(
bits.data(), x + i,
sizeof(
bits));
531 for (; i < count; ++i) {
532 std::uint64_t
bits {};
533 std::memcpy(&
bits, x + i,
sizeof(
bits));
544 template <
typename T>
566 std::atomic<bool> finite {
true};
570 finite.store(
false, std::memory_order_relaxed);
573 return finite.load(std::memory_order_relaxed);
581 throw non_finite_error(std::string {what} +
": the matrix holds an infinite or NaN entry");
589 const auto*
const parts {
reinterpret_cast<const double*
>(std::to_address(a.begin()))};
591 throw non_finite_error(std::string {what} +
": the matrix holds an infinite or NaN entry");
597 template <
typename T>
600 for (std::size_t i {0}; i < a.
rows(); ++i) {
601 bool finite {std::isfinite(
real_part(a(i, i)))};
602 for (std::size_t j {0}; j < i; ++j) {
617 template <
typename T>
622 throw non_finite_error(std::string {what} +
": the matrix holds an infinite or NaN entry");
635 throw non_finite_error(std::string {what} +
": the matrix holds an infinite or NaN entry");
643 const auto*
const parts {
reinterpret_cast<const double*
>(std::to_address(a.begin()))};
645 throw non_finite_error(std::string {what} +
": the matrix holds an infinite or NaN entry");
654 template <
typename T>
661 for (std::size_t t {0}; t <
terms; ++t) {
670 const double* columns,
687 template <
typename T>
709 if (a.cols() !=
n_) {
713 lu_ = std::move(a).elements();
715 for (std::size_t i {0}; i <
n_; ++i) {
731 throw dimension_error(
"solve: right-hand side size differs from the matrix");
737 for (std::size_t i {0}; i <
n_; ++i) {
739 for (std::size_t j {0}; j < i; ++j) {
744 for (std::size_t i {
n_}; i-- > 0;) {
746 for (std::size_t j {i + 1}; j <
n_; ++j) {
758 for (std::size_t i {0}; i <
n_; ++i) {
759 value *=
lu_[(i *
n_) + i];
774 return {
T {0}, -std::numeric_limits<double>::infinity()};
778 for (std::size_t i {0}; i <
n_; ++i) {
809 const std::size_t n {
n_};
810 T*
const at {rows.data()};
811 const std::size_t*
const to {
pivots_.data()};
816 std::copy_n(at + (r * n), n,
held.data());
817 for (std::size_t i {0}; i < n; ++i) {
818 at[(r * n) +
to[i]] =
held[i];
822 return matrix<T> {n, n, std::move(rows)};
838 const std::size_t n {
n_};
843 std::fill_n(rows + (r * n), n,
T {});
844 rows[(r * n) + r] =
T {1};
865 throw dimension_error(
"solve: right-hand side row count differs from the matrix");
870 const std::size_t cols {b.
cols()};
872 for (std::size_t i {0}; i <
n_; ++i) {
873 for (std::size_t c {0}; c < cols; ++c) {
874 rows[(i * cols) + c] = b(
pivots_[i], c);
891 if (b.rows() !=
n_) {
892 throw dimension_error(
"solve: right-hand side row count differs from the matrix");
897 const std::size_t cols {b.cols()};
898 buffer<T> rows {std::move(b).elements()};
900 std::vector<bool>
placed(
n_,
false);
902 for (std::size_t start {0}; start <
n_; ++start) {
906 std::copy_n(rows.data() + (start * cols), cols,
held.data());
907 std::size_t i {start};
909 std::copy_n(rows.data() + (
from * cols), cols, rows.data() + (i * cols));
912 std::copy_n(
held.data(), cols, rows.data() + (i * cols));
958 bool lower =
false)
const
960 if constexpr (std::is_same_v<T, double>) {
964#ifdef NUMERICAL_X86_COMPILED
977 const auto start {[
this,
slice,
slices, cols, lower](std::size_t s) {
996#ifdef NUMERICAL_X86_COMPILED
1015 std::size_t cols)
const
1017 const auto n {
static_cast<double>(
n_)};
1018 const auto weight {[n](
double c) {
return (n * n * c) + (((n * n * n) - ((n - c) * (n - c) * (n - c))) / 3.0); }};
1019 const double target {
weight(
static_cast<double>(cols)) *
static_cast<double>(s) /
static_cast<double>(
slices)};
1020 std::size_t low {0};
1021 std::size_t high {cols};
1022 while (low < high) {
1023 const std::size_t
middle {low + ((high - low) / 2)};
1031 return std::min(cols, (low + 7) / 8 * 8);
1044 constexpr std::size_t width {64};
1045 const std::size_t chunks {(
c1 -
c0 + width - 1) / width};
1047 const std::size_t end {std::min(
k0 +
block_,
n_)};
1048 const std::size_t
reach {lower ? std::min(end,
c1) :
c1};
1053 const std::size_t first {
c0 + (chunk * width)};
1054 const std::size_t
last {std::min(first + width,
reach)};
1055 for (std::size_t i {
k0 + 1}; i < end; ++i) {
1061 for (std::size_t
k1 {
n_};
k1 > 0;) {
1064 const std::size_t first {
c0 + (chunk * width)};
1065 const std::size_t
last {std::min(first + width,
c1)};
1066 for (std::size_t i {
k1}; i-- >
k0;) {
1069 for (std::size_t c {first}; c <
last; ++c) {
1089 std::size_t cols)
const
1100 for (std::size_t i {
i0 + 1}; i <
i0 +
group; ++i) {
1104 for (std::size_t i {std::max<std::size_t>(
i0, 1)}; i <
n_; ++i) {
1112 const auto finish {[
this, &
y, cols](std::size_t i) {
1118 for (std::size_t c {0}; c < cols; ++c) {
1122 std::size_t
i1 {
n_};
1132 for (std::size_t i {
i1}; i-- >
top;) {
1141 return i & ~(
block_ - 1);
1153 std::size_t
last)
const
1158 y.data() + (
r0 * cols) + first, cols);
1167 std::size_t
c1)
const
1170 for (std::size_t r {
r0}; r <
r1; ++r) {
1171 for (std::size_t c {
c0}; c <
c1; ++c) {
1181 std::size_t
c1)
const
1184 for (std::size_t r {
r0}; r <
r1; ++r) {
1185 for (std::size_t c {
c0}; c <
c1; ++c) {
1213 const std::size_t end {std::min(
block_,
n_)};
1235 for (std::size_t
k0 {0}; end <
n_;) {
1236 const std::size_t next {std::min(end +
block_,
n_)};
1241 const std::size_t right {
n_ - next};
1254 const std::size_t width {std::max(
block_, (right + (2 * helpers) - 1) / (2 * helpers))};
1255 const std::size_t blocks {(right + width - 1) / width};
1258 [&](std::size_t
block) {
1259 const std::size_t first {next + (
block * width)};
1265 for (std::size_t c {next}; c <
n_; ++c) {
1266 std::swap(
lu_[(a *
n_) + c],
lu_[(b *
n_) + c]);
1287 const std::size_t rows {
n_ -
k0};
1288 const std::size_t width {end -
k0};
1304 const std::size_t width {end -
k0};
1305#ifdef NUMERICAL_X86_COMPILED
1311 for (std::size_t j {0}; j < width; ++j) {
1312 for (std::size_t r {
r0}; r <
r1; ++r) {
1318 for (std::size_t j {0}; j < width; ++j) {
1319 for (std::size_t r {
r0}; r <
r1; ++r) {
1328 for (std::size_t r {0}; r < rows; ++r) {
1329 for (std::size_t j {0}; j < width; ++j) {
1359 for (std::size_t j {first}; j <
last; ++j) {
1361 std::size_t
pivot {0};
1376 for (std::size_t c {0}; c < end -
k0; ++c) {
1384 for (std::size_t r {j + 1}; r < rows; ++r) {
1387 for (std::size_t c {j + 1}; c <
last; ++c) {
1394 const std::size_t
mid {first + ((
last - first) / 2)};
1396 for (std::size_t c {
mid}; c <
last; ++c) {
1398 for (std::size_t j {first}; j <
mid; ++j) {
1404 const std::size_t depth {
mid - first};
1406 for (std::size_t c {
mid}; c <
last; ++c) {
1407 for (std::size_t j {first}; j <
mid; ++j) {
1430 std::swap(
lu_[(a *
n_) + c],
lu_[(b *
n_) + c]);
1449 const std::size_t c0 {p * block_};
1452 [](
const auto& swap, std::size_t
row) { return swap.first < row; })};
1453 for (
auto swap {first}; swap !=
left_swaps_.end(); ++swap) {
1454 std::swap_ranges(
lu_.data() + (swap->first *
n_) +
c0,
lu_.data() + (swap->first *
n_) +
c1,
1455 lu_.data() + (swap->second *
n_) +
c0);
1467 constexpr std::size_t width {64};
1468 const std::size_t blocks {((n_ - end) + width - 1) / width};
1470 const std::size_t first {end + (
block * width)};
1471 const std::size_t
last {std::min(first + width, n_)};
1472 for (std::size_t i {
k0 + 1}; i < end; ++i) {
1485 const std::size_t end {
k0 + left.
k};
1494 bool singular_ {
false};
1495 std::size_t swap_limit_ {n_};
1508 template <
typename T>
1524 template <
typename T>
1532 template <
typename T>
1545 template <
typename T>
1558 std::size_t order = 0)
1559 : std::runtime_error(message), order_ {order}
1563 [[nodiscard]] std::size_t
order() const noexcept
1583 template <
typename T>
1608 if (a.cols() != n_) {
1609 throw dimension_error(
"Cholesky decomposition requires a square matrix");
1611 detail::require_finite_lower(a,
"Cholesky decomposition");
1612 lower_ = std::move(a).elements();
1625 return matrix<T> {n_, n_, std::move(lower_)};
1636 if (b.
size() != n_) {
1637 throw dimension_error(
"solve: right-hand side size differs from the matrix");
1640 for (std::size_t i {0}; i < n_; ++i) {
1642 for (std::size_t k {0}; k < i; ++k) {
1643 sum -= lower_[(i * n_) + k] * y[k];
1645 y[i] =
sum / lower_[(i * n_) + i];
1648 for (std::size_t i {n_}; i-- > 0;) {
1650 for (std::size_t k {i + 1}; k < n_; ++k) {
1651 sum -= detail::conj_of(lower_[(k * n_) + i]) * x[k];
1653 x[i] =
sum / detail::conj_of(lower_[(i * n_) + i]);
1661 T value {
static_cast<T
>(1)};
1662 for (std::size_t i {0}; i < n_; ++i) {
1663 value *= lower_[(i * n_) + i] * lower_[(i * n_) + i];
1678 for (std::size_t k0 {0}; k0 < n_; k0 += block_) {
1679 const std::size_t end {std::min(k0 + block_, n_)};
1681 for (; end - k0 >= detail::cholesky_together_width && i + detail::cholesky_diagonal_rows <= end; i += detail::cholesky_diagonal_rows) {
1682 factor_rows_together(i, k0);
1683 for (std::size_t r {0}; r < detail::cholesky_diagonal_rows; ++r) {
1684 factor_row(i + r, k0, i, i + r + 1);
1687 for (; i < end; ++i) {
1688 factor_row(i, k0, k0, i + 1);
1691 minus_adjoint.resize((end - k0) * (n_ - end));
1692 factor_rows_below(k0, end, minus_adjoint);
1693 update_trailing(k0, end, minus_adjoint, packing);
1696 T*
const rows {lower_.data()};
1697 const std::size_t n {n_};
1699 detail::parallel_for((n + detail::zeroed_rows - 1) / detail::zeroed_rows, detail::memory_element_weight * detail::zeroed_rows * n / 2, [rows, n](std::size_t band) {
1700 for (std::size_t i {band * detail::zeroed_rows}; i < std::min(n, (band + 1) * detail::zeroed_rows); ++i) {
1701 std::fill(rows + (i * n) + i + 1, rows + ((i + 1) * n), T {});
1716 std::array<T, detail::cholesky_diagonal_rows> sums {};
1717 for (std::size_t j {k0}; j < i; ++j) {
1718 const T*
const above {lower_.data() + (j * n_)};
1720 for (std::size_t r {0}; r < detail::cholesky_diagonal_rows; ++r) {
1721 sums[r] = lower_[((i + r) * n_) + j];
1723 for (std::size_t k {k0}; k < j; ++k) {
1724 const T other {detail::conj_of(above[k])};
1726 for (std::size_t r {0}; r < detail::cholesky_diagonal_rows; ++r) {
1727 sums[r] -= lower_[((i + r) * n_) + k] * other;
1731 for (std::size_t r {0}; r < detail::cholesky_diagonal_rows; ++r) {
1732 lower_[((i + r) * n_) + j] = sums[r] / above[j];
1747 for (std::size_t j {first}; j < last; ++j) {
1748 T
sum {lower_[(i * n_) + j]};
1749 for (std::size_t k {k0}; k < j; ++k) {
1750 sum -= lower_[(i * n_) + k] * detail::conj_of(lower_[(j * n_) + k]);
1753 const double diagonal {detail::real_part(
sum)};
1754 if (diagonal <= 0.0) {
1757 lower_[(i * n_) + i] = detail::from_double<T>(std::sqrt(diagonal));
1760 lower_[(i * n_) + j] =
sum / lower_[(j * n_) + j];
1781 const std::size_t width {end - k0};
1782 const std::size_t chunks {((n_ - end) + detail::cholesky_rows - 1) / detail::cholesky_rows};
1783 detail::parallel_for(chunks, detail::cholesky_rows * width * width / 2, [&](std::size_t chunk) {
1784 const std::size_t first {end + (chunk * detail::cholesky_rows)};
1785 const std::size_t height {std::min(detail::cholesky_rows, n_ - first)};
1787 for (std::size_t r {0}; r < height; ++r) {
1788 for (std::size_t j {0}; j < width; ++j) {
1789 panel[(j * height) + r] = lower_[((first + r) * n_) + k0 + j];
1792 constexpr std::size_t sub {16};
1794 for (std::size_t s0 {0}; s0 < width; s0 += sub) {
1795 const std::size_t s1 {std::min(s0 + sub, width)};
1797 minus_conj.resize((s1 - s0) * s0);
1798 for (std::size_t j {s0}; j < s1; ++j) {
1799 for (std::size_t k {0}; k < s0; ++k) {
1800 minus_conj[((j - s0) * s0) + k] = -detail::conj_of(lower_[((k0 + j) * n_) + k0 + k]);
1806 detail::gemm_rows(s1 - s0, height, s0, minus_conj.data(), s0, panel.data(), height, panel.data() + (s0 * height), height);
1808 for (std::size_t j {s0}; j < s1; ++j) {
1809 T*
const column {panel.data() + (j * height)};
1810 detail::subtract_panel_columns(column, panel.data() + (s0 * height), height, lower_.data() + ((k0 + j) * n_) + k0 + s0, j - s0);
1811 const T pivot {lower_[((k0 + j) * n_) + k0 + j]};
1812 for (std::size_t r {0}; r < height; ++r) {
1813 column[r] = column[r] / pivot;
1817 const std::size_t below {n_ - end};
1818#ifdef NUMERICAL_X86_COMPILED
1822 for (std::size_t r {0}; r < height; ++r) {
1823 for (std::size_t j {0}; j < width; ++j) {
1824 lower_[((first + r) * n_) + k0 + j] = panel[(j * height) + r];
1827 for (std::size_t j {0}; j < width; ++j) {
1828 const T*
const from {panel.data() + (j * height)};
1829 T*
const to {minus_adjoint.data() + (j * below) + (first - end)};
1830 for (std::size_t r {0}; r < height; ++r) {
1831 to[r] = -detail::conj_of(from[r]);
1835 for (std::size_t r {0}; r < height; ++r) {
1836 for (std::size_t j {0}; j < width; ++j) {
1837 const T value {panel[(j * height) + r]};
1838 lower_[((first + r) * n_) + k0 + j] = value;
1839 minus_adjoint[(j * below) + (first - end + r)] = -detail::conj_of(value);
1862 const std::size_t rows {n_ - end};
1863 const std::size_t depth {end - k0};
1864#ifdef NUMERICAL_X86_COMPILED
1870 if constexpr (std::is_same_v<T, double>) {
1871 if (depth >= detail::gemm_min_depth && rows * rows >= ((detail::gemm_packed_threshold + depth) - 1) / depth) {
1872 detail::gemm_triangle<detail::a_rows, detail::b_rows>(rows, depth, lower_.data() + (end * n_) + k0, n_, minus_adjoint.data(), rows,
1873 lower_.data() + (end * n_) + end, n_,
true);
1878 const std::size_t step {std::max(block_, detail::cholesky_update_columns)};
1881 const bool shared {rows > 2 * detail::cholesky_update_rows};
1882 const T*
const l21 {lower_.data() + (end * n_) + k0};
1884 : detail::read_in_place(rows, depth, l21, n_)};
1885 const std::size_t granule {std::max<std::size_t>(1, left.granule)};
1886 const std::size_t height {std::max(granule, detail::cholesky_update_rows / granule * granule)};
1887 std::vector<std::pair<std::size_t, std::size_t>> tiles;
1888 for (std::size_t jb {0}; jb < rows; jb += step) {
1889 for (std::size_t r0 {jb / granule * granule}; r0 < rows; r0 += height) {
1890 tiles.emplace_back(jb, r0);
1893 detail::parallel_for(tiles.size(), height * step * depth / detail::gemm_kernel_speedup, [&](std::size_t t) {
1894 const auto [jb, r0] {tiles[t]};
1895 detail::gemm_with(detail::left_rows(left, r0, std::min(height, rows - r0)), std::min(step, rows - jb),
1896 minus_adjoint.data() + jb, rows, lower_.data() + ((end + r0) * n_) + end + jb, n_);
1901 std::size_t block_ {detail::cholesky_block_for(n_)};
1906 template <
typename T>
1916 template <
typename T>
1936 template <
typename T>
1943 constexpr std::size_t width {64};
1944 const std::size_t span {cols - from};
1945 parallel_for((span + width - 1) / width, 2 * v.size() * width, [&](std::size_t chunk) {
1946 const std::size_t start {from + (chunk * width)};
1947 const std::size_t count {std::min(width, cols - start)};
1949 for (std::size_t i {0}; i < v.size(); ++i) {
1950 const T weight {
conj_of(v[i])};
1951 const T* row {a.data() + ((first + i) * cols) + start};
1954 for (std::size_t i {0}; i < v.size(); ++i) {
1955 const T scale {
static_cast<T
>(2) * v[i]};
1956 T* row {a.data() + ((first + i) * cols) + start};
1963 template <
typename T>
1978 template <
typename T>
1983 const std::vector<T>* taus =
nullptr)
1986 const std::size_t b {block.width};
1987 block.v.assign(block.height * b, T {});
1988 block.t.assign(b * b, T {});
1989 for (std::size_t j {0}; j < b; ++j) {
1990 const buffer<T>& v {reflectors[k0 + j]};
1991 for (std::size_t r {0}; r < v.size(); ++r) {
1992 block.v[((j + r) * b) + j] = v[r];
1996 buffer<T> v_adjoint(b * block.height, T {});
1997 for (std::size_t r {0}; r < block.height; ++r) {
1998 for (std::size_t j {0}; j < b; ++j) {
1999 v_adjoint[(j * block.height) + r] =
conj_of(block.v[(r * b) + j]);
2003 gemm(b, b, block.height, v_adjoint.data(), block.height, block.v.data(), b, gram.data(), b);
2004 for (std::size_t j {0}; j < b; ++j) {
2005 if (reflectors[k0 + j].empty()) {
2008 const T tau {taus ==
nullptr ?
static_cast<T
>(2) : (*taus)[k0 + j]};
2009 for (std::size_t i {0}; i < j; ++i) {
2011 for (std::size_t p {i}; p < j; ++p) {
2012 sum += block.t[(i * b) + p] * gram[(p * b) + j];
2014 block.t[(i * b) + j] = -tau *
sum;
2016 block.t[(j * b) + j] = tau;
2026 template <
typename T>
2034 const std::size_t b {block.
width};
2035 const std::size_t h {block.
height};
2036 const std::size_t w {to - from};
2039 for (std::size_t r {0}; r < h; ++r) {
2040 for (std::size_t j {0}; j < b; ++j) {
2041 v_adjoint[(j * h) + r] =
conj_of(block.
v[(r * b) + j]);
2042 minus_v[(r * b) + j] = -block.
v[(r * b) + j];
2045 T* top {a.data() + (block.
first * cols) + from};
2047 gemm(b, w, h, v_adjoint.data(), h, top, cols, projection.data(), w);
2049 for (std::size_t i {0}; i < b; ++i) {
2050 for (std::size_t p {0}; p < b; ++p) {
2051 const T factor {
adjoint ?
conj_of(block.
t[(p * b) + i]) : block.
t[(i * b) + p]};
2052 if (factor == T {}) {
2055 add_product(scaled.data() + (i * w), factor, projection.data() + (p * w), w);
2058 gemm(h, w, b, minus_v.data(), b, scaled.data(), w, top, cols);
2069 template <
typename T>
2075 bool adjoint =
false)
2077 const std::size_t b {block.
width};
2078 const std::size_t h {block.
height};
2079 const std::size_t r {to - from};
2080 const auto v_conjugate {std::make_unique_for_overwrite<T[]>(h * b)};
2081 const auto minus_v_t {std::make_unique_for_overwrite<T[]>(b * h)};
2082 const auto t_transposed {std::make_unique_for_overwrite<T[]>(b * b)};
2083 for (std::size_t row {0}; row < h; ++row) {
2084 for (std::size_t j {0}; j < b; ++j) {
2085 v_conjugate[(row * b) + j] =
conj_of(block.
v[(row * b) + j]);
2086 minus_v_t[(j * h) + row] = -block.
v[(row * b) + j];
2089 for (std::size_t i {0}; i < b; ++i) {
2090 for (std::size_t p {0}; p < b; ++p) {
2091 t_transposed[(p * b) + i] =
adjoint ?
conj_of(block.
t[(p * b) + i]) : block.
t[(i * b) + p];
2094 T* left {at.data() + (from * cols) + block.
first};
2096 gemm(r, b, h, left, cols, v_conjugate.get(), b, projection.data(), b);
2098 gemm(r, b, b, projection.data(), b, t_transposed.get(), b, scaled.data(), b);
2099 gemm(r, h, b, scaled.data(), b, minus_v_t.get(), h, left, cols);
2103 template <
typename T>
2104 requires std::is_floating_point_v<T>
2113 template <
typename T>
2126 template <
typename T>
2135 for (std::size_t j {from}; j < to; ++j) {
2136 T*
const column {panel + (j * height) + first};
2137 const T scaled {s *
conjugate_dot(v.data(), column, v.size())};
2158 template <
typename T>
2163 std::vector<T>& taus,
2167 for (std::size_t k {k0}; k < k1; ++k) {
2168 const std::size_t c {k - k0};
2169 T*
const x {panel + (c * ld) + (k - top)};
2172 const T alpha {w[0]};
2173 const double below {
scaled_norm(w.data() + 1, w.size() - 1)};
2174 if (below == 0.0 && alpha == detail::from_double<T>(detail::real_part(alpha))) {
2177 const double beta {-std::copysign(
scaled_norm(w), detail::real_part(alpha))};
2178 const T tau {(detail::from_double<T>(beta) - alpha) / detail::from_double<T>(beta)};
2179 const T scale {T {1} / (alpha - detail::from_double<T>(beta))};
2181 for (std::size_t r {1}; r < w.size(); ++r) {
2185 x[0] = detail::from_double<T>(beta / factor);
2186 std::copy(w.begin() + 1, w.end(), x + 1);
2187 reflectors[k] = std::move(w);
2193 template <
typename T>
2208 template <
typename T>
2213 const std::size_t depth {std::min(rows, cols)};
2214 std::vector<buffer<T>> reflectors(depth);
2215 std::vector<T> taus(depth, T {});
2216 std::vector<reflector_block<T>> blocks;
2217 for (std::size_t k0 {0}; k0 < depth; k0 +=
qr_block) {
2218 const std::size_t k1 {std::min(k0 +
qr_block, depth)};
2219 factor_qr_panel(columns.data() + (k0 * rows), rows, 0, reflectors, taus, k0, k1);
2234 template <
typename T>
2245 to[(j * rows) + i] = from[(i * cols) + j];
2257 template <
typename T>
2262 std::vector<T>& taus,
2266 const std::size_t height {rows - k0};
2267 const std::size_t width {k1 - k0};
2269 for (std::size_t i {0}; i < height; ++i) {
2270 for (std::size_t j {0}; j < width; ++j) {
2271 panel[(j * height) + i] = a[((k0 + i) * cols) + k0 + j];
2275 for (std::size_t i {0}; i < height; ++i) {
2276 for (std::size_t j {0}; j < width; ++j) {
2277 a[((k0 + i) * cols) + k0 + j] = panel[(j * height) + i];
2291 template <
typename T>
2297 const std::size_t depth {std::min(rows, cols)};
2298 std::vector<buffer<T>> reflectors(depth);
2299 std::vector<T> taus(depth, T {});
2303 std::vector<reflector_block<T>> blocks;
2304 for (std::size_t k0 {0}; k0 < depth; k0 +=
qr_block) {
2305 const std::size_t k1 {std::min(k0 +
qr_block, depth)};
2312 for (std::size_t i {0}; r !=
nullptr && i < depth; ++i) {
2313 for (std::size_t j {i}; j < cols; ++j) {
2314 (*r)[(i * cols) + j] = a[(i * cols) + j];
2328 template <
typename T>
2333#ifdef NUMERICAL_X86_COMPILED
2334 const std::size_t depth {std::min(rows, cols)};
2342 out.r[(i * cols) + j] = i <= j ? columns[(j * rows) + i] : T {};
2362 template <
typename T>
2370#ifdef NUMERICAL_X86_COMPILED
2372 buffer<T>& qt {transposed ? out : formed};
2374 for (std::size_t c {0}; c < count; ++c) {
2375 qt[(c * rows) + first + c] = T {1};
2377 for (std::size_t b {blocks.size()}; b-- > 0;) {
2381 out.resize(rows * count);
2386 buffer<T>& q {transposed ? formed : out};
2388 for (std::size_t c {0}; c < count; ++c) {
2389 q[((first + c) * count) + c] = T {1};
2391 for (std::size_t b {blocks.size()}; b-- > 0;) {
2395 out.resize(count * rows);
2418 template <
typename T>
2422 bool complete =
false)
2424 const std::size_t depth {std::min(rows, cols)};
2425 const std::size_t width {
complete ? rows : depth};
2430 const auto formed {[&](std::size_t first, std::size_t count) {
2432 form_q_into(columns, factors.blocks, rows, first, count,
false);
2435 thin_qr<T> out {.
q = formed(0, depth), .r = std::move(factors.r)};
2436 if (width > depth) {
2437 const buffer<T> rest {formed(depth, width - depth)};
2439 for (std::size_t i {0}; i < rows; ++i) {
2440 std::copy_n(out.q.data() + (i * depth), depth, whole.data() + (i * width));
2441 std::copy_n(rest.data() + (i * (width - depth)), width - depth, whole.data() + (i * width) + depth);
2443 out.q = std::move(whole);
2445 for (std::size_t k {0}; k < depth; ++k) {
2446 const T phase {
phase_of(out.r[(k * cols) + k])};
2447 for (std::size_t j {k}; j < cols; ++j) {
2448 out.r[(k * cols) + j] *=
conj_of(phase);
2450 for (std::size_t i {0}; i < rows; ++i) {
2451 out.q[(i * width) + k] *= phase;
2463 return largest * std::numeric_limits<double>::epsilon() *
static_cast<double>(std::max(rows, cols));
2501 std::size_t cols)
const noexcept
2506 return value_ < 0.0 ? detail::rank_tolerance(largest, rows, cols) : largest * value_;
2512 return absolute_ ?
rank_cutoff {std::ldexp(value_, -exponent),
true} : *
this;
2518 bool absolute) noexcept
2519 : value_(value), absolute_(absolute)
2524 if (!(value >= 0.0)) {
2525 throw std::invalid_argument(
"rank cutoff: a tolerance is non-negative");
2530 double value_ {-1.0};
2531 bool absolute_ {
false};
2553 template <
typename T>
2564 qr_mode mode = qr_mode::reduced)
2576 qr_mode mode = qr_mode::reduced)
2577 : rows_(a.rows()), cols_(a.cols()), depth_(std::
min(rows_, cols_)),
2580 detail::require_finite(a,
"QR decomposition");
2581 detail::thin_qr<T> factors {detail::householder_qr(std::move(a).elements(), rows_, cols_, mode == qr_mode::complete)};
2582 q_ = std::move(factors.q);
2583 r_ = std::move(factors.r);
2584 r_.resize(width_ * cols_, T {});
2596 return matrix<T> {rows_, width_, std::move(q_)};
2608 return matrix<T> {width_, cols_, std::move(r_)};
2622 if (b.
size() != rows_) {
2623 throw dimension_error(
"solve: right-hand side size differs from the matrix");
2625 if (rows_ < cols_) {
2626 throw dimension_error(
"QR solve: the system is underdetermined (lstsq gives its minimum-norm solution)");
2628 double largest {0.0};
2629 for (std::size_t i {0}; i < cols_; ++i) {
2630 largest = std::max(largest, detail::real_part(r_[(i * cols_) + i]));
2632 const double tolerance {detail::rank_tolerance(largest, rows_, cols_)};
2634 for (std::size_t i {0}; i < cols_; ++i) {
2635 if (detail::real_part(r_[(i * cols_) + i]) <= tolerance) {
2636 throw singular_matrix_error(
"QR solve: the matrix is rank-deficient (lstsq gives its minimum-norm solution)");
2639 for (std::size_t k {0}; k < rows_; ++k) {
2640 sum += detail::conj_of(q_[(k * width_) + i]) * b[k];
2645 for (std::size_t i {cols_}; i-- > 0;) {
2647 for (std::size_t j {i + 1}; j < cols_; ++j) {
2648 sum -= r_[(i * cols_) + j] * x[j];
2650 x[i] =
sum / r_[(i * cols_) + i];
2666 template <
typename T>
2679 : std::runtime_error(message)
2690 template <
typename T>
2691 requires std::is_floating_point_v<T>
2706 template <
typename T>
2712 using R =
typename T::value_type;
2713 const R c {f.real()};
2714 const R d {f.imag()};
2715 for (std::size_t i {0}; i < count; ++i) {
2716 const R a {x[i].real()};
2717 const R b {-x[i].imag()};
2722 const R re {ac - bd};
2723 const R im {ad + bc};
2724 y[i] = T {y[i].real() + re, y[i].imag() + im};
2733 double*, std::size_t);
2739 template <
typename T>
2750#ifdef NUMERICAL_NEON_COMPILED
2757 inline std::complex<double> neon_hermitian_row(
const double* row,
2762 std::size_t count)
noexcept
2764 const float64x2_t real {vr, -vr};
2765 const float64x2_t imag {vi, vi};
2766 float64x2_t same[2] {vdupq_n_f64(0.0), vdupq_n_f64(0.0)};
2767 float64x2_t across[2] {vdupq_n_f64(0.0), vdupq_n_f64(0.0)};
2769 for (; j + 2 <= count; j += 2) {
2770 for (std::size_t l {0}; l < 2; ++l) {
2771 const float64x2_t r {vld1q_f64(row + (2 * (j + l)))};
2772 const float64x2_t x {vld1q_f64(v + (2 * (j + l)))};
2773 const float64x2_t s {vextq_f64(r, r, 1)};
2774 float64x2_t o {vld1q_f64(out + (2 * (j + l)))};
2775 o = vfmaq_f64(o, real, r);
2776 o = vfmaq_f64(o, imag, s);
2777 same[l] = vfmaq_f64(same[l], r, x);
2778 across[l] = vfmaq_f64(across[l], s, x);
2779 vst1q_f64(out + (2 * (j + l)), o);
2782 const float64x2_t sum_same {vaddq_f64(same[0], same[1])};
2783 const float64x2_t sum_across {vaddq_f64(across[0], across[1])};
2784 double re {vgetq_lane_f64(sum_same, 0) - vgetq_lane_f64(sum_same, 1)};
2785 double im {vgetq_lane_f64(sum_across, 0) + vgetq_lane_f64(sum_across, 1)};
2786 for (; j < count; ++j) {
2787 const double rr {row[2 * j]};
2788 const double ri {row[(2 * j) + 1]};
2789 out[2 * j] = std::fma(vi, ri, std::fma(vr, rr, out[2 * j]));
2790 out[(2 * j) + 1] = std::fma(vi, rr, std::fma(-vr, ri, out[(2 * j) + 1]));
2791 re = std::fma(-ri, v[(2 * j) + 1], std::fma(rr, v[2 * j], re));
2792 im = std::fma(rr, v[(2 * j) + 1], std::fma(ri, v[2 * j], im));
2794 return std::complex<double> {re, im};
2804 const std::complex<double>* row,
2805 const std::complex<double>* v,
2806 std::complex<double>* out,
2809#ifdef NUMERICAL_X86_COMPILED
2812 if (path == gemm_path::avx512 || path == gemm_path::avx2) {
2813 const auto*
const values {
reinterpret_cast<const double*
>(v)};
2814 const auto*
const parts {
reinterpret_cast<const double*
>(row)};
2815 auto*
const into {
reinterpret_cast<double*
>(out)};
2816 const std::complex<double> lower {path == gemm_path::avx512
2817 ? avx512_hermitian_row(parts, values, v[i].real(), v[i].imag(), into, i)
2818 : avx2_hermitian_row(parts, values, v[i].real(), v[i].imag(), into, i)};
2819 return lower + (row[i] * v[i]);
2822#ifdef NUMERICAL_NEON_COMPILED
2824 const auto*
const values {
reinterpret_cast<const double*
>(v)};
2825 const auto*
const parts {
reinterpret_cast<const double*
>(row)};
2826 auto*
const into {
reinterpret_cast<double*
>(out)};
2827 const std::complex<double> lower {neon_hermitian_row(parts, values, v[i].real(), v[i].imag(), into, i)};
2828 return lower + (row[i] * v[i]);
2831 return hermitian_row<std::complex<double>>(kernel, row, v, out, i);
2841 return kernel(row, v, v[i], out, i) + (row[i] * v[i]);
2850 template <
typename T>
2853#ifdef NUMERICAL_X86_COMPILED
2854 if constexpr (std::is_same_v<T, double>) {
2878 template <
typename T>
2886 constexpr std::size_t rows_per_task {32};
2887 const std::size_t tasks {(m + rows_per_task - 1) / rows_per_task};
2888 if (scratch.size() < tasks * m) {
2889 scratch.resize(tasks * m, T {});
2891 symmetric_row_kernel kernel {[](
const double* row,
const double* x,
double f,
double* out, std::size_t count) {
2894 symmetric_rows_kernel group {[](
const double* rows, std::size_t ld,
const double* x,
double* out, std::size_t i) {
2897#ifdef NUMERICAL_X86_COMPILED
2898 if constexpr (std::is_same_v<T, double>) {
2900 if (path == gemm_path::avx512) {
2901 kernel = &avx512_symmetric_row;
2902 group = &avx512_symmetric_rows;
2904 else if (path == gemm_path::avx2) {
2905 kernel = &avx2_symmetric_row;
2906 group = &avx2_symmetric_rows;
2910 parallel_for(tasks, rows_per_task * m * symmetric_row_speedup<T>(), [&](std::size_t task) {
2911 const std::size_t first {task * rows_per_task};
2912 const std::size_t last {std::min(first + rows_per_task, m)};
2913 T* own {scratch.data() + (task * m)};
2914 std::fill(own, own + last, T {});
2915 std::size_t i {first};
2916 if constexpr (std::is_same_v<T, double>) {
2918 const std::array<double, symmetric_row_group> sums {group(block + (i * n), n, v, own, i)};
2920 own[i + r] += sums[r];
2924 for (; i < last; ++i) {
2925 own[i] +=
hermitian_row(kernel, block + (i * n), v, own, i);
2928 std::fill(w, w + m, T {});
2929 for (std::size_t task {0}; task < tasks; ++task) {
2930 add_product(w, T {1}, scratch.data() + (task * m), std::min((task + 1) * rows_per_task, m));
2937#ifdef NUMERICAL_X86_COMPILED
2938 inline constexpr std::size_t corrected_product_least {256};
2944 template <
typename T>
2945 requires std::is_floating_point_v<T>
2963 template <split_complex T>
2971#ifdef NUMERICAL_X86_COMPILED
2972 if constexpr (std::is_same_v<T, std::complex<double>>) {
2979 using R =
typename T::value_type;
2980 for (std::size_t t {0}; t < terms; ++t) {
2981 const R c {f[t].real()};
2982 const R d {f[t].imag()};
2983 const T*
const row {x + (t * ldx)};
2984 for (std::size_t i {0}; i < count; ++i) {
2985 const R a {row[i].real()};
2986 const R b {row[i].imag()};
2991 const R re {ac - bd};
2992 const R im {ad + bc};
2993 y[i] = T {y[i].real() - re, y[i].imag() - im};
3017 template <
typename T>
3024 const T* corrections,
3029 const T* next_factors =
nullptr)
3031 constexpr std::size_t rows_per_task {32};
3032 const std::size_t tasks {(m + rows_per_task - 1) / rows_per_task};
3033 if (scratch.size() < tasks * m) {
3034 scratch.resize(tasks * m, T {});
3036 symmetric_row_kernel kernel {[](
const double* row,
const double* x,
double f,
double* out, std::size_t count) {
3039 symmetric_rows_kernel group {[](
const double* rows, std::size_t ld,
const double* x,
double* out, std::size_t i) {
3042#ifdef NUMERICAL_X86_COMPILED
3043 if constexpr (std::is_same_v<T, double>) {
3045 if (path == gemm_path::avx512) {
3046 kernel = &avx512_symmetric_row;
3047 group = &avx512_symmetric_rows;
3049 else if (path == gemm_path::avx2) {
3050 kernel = &avx2_symmetric_row;
3051 group = &avx2_symmetric_rows;
3058 const std::size_t pairs {terms / 2};
3059 const auto coefficients = [&](std::size_t p) {
3060 factors[2 * p] =
conjugate_dot(corrections + (((2 * p) + 1) * ldc), v, m);
3061 factors[(2 * p) + 1] =
conjugate_dot(corrections + ((2 * p) * ldc), v, m);
3063 parallel_for(tasks + (share ? pairs : 0), rows_per_task * m * symmetric_row_speedup<T>(), [&](std::size_t task) {
3064 if (task >= tasks) {
3065 coefficients(task - tasks);
3068 const std::size_t first {task * rows_per_task};
3069 const std::size_t last {std::min(first + rows_per_task, m)};
3070 T* own {scratch.data() + (task * m)};
3071 std::fill(own, own + last, T {});
3072 std::size_t i {first};
3073 if constexpr (std::is_same_v<T, double>) {
3075 const std::array<double, symmetric_row_group> sums {group(block + (i * n), n, v, own, i)};
3077 own[i + r] += sums[r];
3081 for (; i < last; ++i) {
3082 own[i] +=
hermitian_row(kernel, block + (i * n), v, own, i);
3085 for (std::size_t p {0}; !share && p < pairs; ++p) {
3088 constexpr std::size_t band {256};
3089 const auto bands = [&](std::size_t b) {
3090 const std::size_t first {b * band};
3091 const std::size_t count {std::min(band, m - first)};
3092 std::fill(w + first, w + first + count, T {});
3093 for (std::size_t task {0}; task < tasks; ++task) {
3094 const std::size_t reach {std::min((task + 1) * rows_per_task, m)};
3095 if (reach > first) {
3096 add_product(w + first, T {1}, scratch.data() + (task * m) + first, std::min(count, reach - first));
3100 if (next !=
nullptr) {
3105 parallel_for((m + band - 1) / band, band * (tasks + terms) * 8, bands);
3108 for (std::size_t b {0}; b < (m + band - 1) / band; ++b) {
3144 std::complex<double> delta,
3145 std::complex<double>& next)
3147 next = delta * std::conj(
phase_of(t));
3152 template <
typename T>
3173 template <
typename T>
3179 std::vector<buffer<T>> reflectors(n);
3183 const auto left {std::make_unique_for_overwrite<T[]>(n * 2 *
tridiagonal_block)};
3184 const auto right {std::make_unique_for_overwrite<T[]>(2 *
tridiagonal_block * n)};
3191 bool column_ahead {
false};
3197 const std::size_t width {k1 - k0};
3198 std::fill_n(by_row.data(), 2 * width * n, T {});
3199 for (std::size_t i {k0}; i < n; ++i) {
3200 for (std::size_t k {k0}; k < std::min(k1, i + 1); ++k) {
3201 columns[((k - k0) * n) + i] = a[(i * n) + k];
3204 for (std::size_t k {k0}; k < k1; ++k) {
3205 const std::size_t j {k - k0};
3206 const std::size_t m {n - k - 1};
3207 T*
const column {columns.data() + (j * n) + k};
3209 const std::size_t from {column_ahead ? j - 1 : 0};
3210 for (std::size_t p {from}; p < j; ++p) {
3211 factors[2 * (p - from)] =
conj_of(by_row[(((2 * p) + 1) * n) + k]);
3212 factors[(2 * (p - from)) + 1] =
conj_of(by_row[((2 * p) * n) + k]);
3214 subtract_corrections(column, factors.data(), by_row.data() + ((2 * from) * n) + k, n, 2 * (j - from), m + 1);
3215 column_ahead =
false;
3216 a[(k * n) + k] = column[0];
3217 buffer<T> v(column + 1, column + m + 1);
3218 const double below {m > 1 ?
scaled_norm(v.data() + 1, m - 1) : 0.0};
3225 sub[k] = alpha / up;
3228 for (T& element : v) {
3231 const T* block {a.data() + ((k + 1) * n) + k + 1};
3235 T*
const next {k + 1 < k1 && j > 0 ? columns.data() + ((j + 1) * n) + k + 1 :
nullptr};
3236 for (std::size_t p {0}; next !=
nullptr && p < j; ++p) {
3237 next_factors[2 * p] =
conj_of(by_row[(((2 * p) + 1) * n) + k + 1]);
3238 next_factors[(2 * p) + 1] =
conj_of(by_row[((2 * p) * n) + k + 1]);
3240 corrected_hermitian_product(block, n, m, v.data(), w.data(), symv_scratch, by_row.data() + k + 1, n, 2 * j, factors.data(),
3241 next, next_factors.data());
3242 column_ahead = next !=
nullptr;
3244 for (std::size_t i {0}; i < m; ++i) {
3245 w[i] = (
static_cast<T
>(2) * w[i]) - (
static_cast<T
>(2) * c * v[i]);
3246 by_row[((2 * j) * n) + k + 1 + i] = v[i];
3247 by_row[(((2 * j) + 1) * n) + k + 1 + i] = w[i];
3249 reflectors[k + 1] = std::move(v);
3256 const std::size_t rest {n - k1};
3257 const std::size_t depth {2 * width};
3258 for (std::size_t i {0}; i < rest; ++i) {
3259 for (std::size_t p {0}; p < width; ++p) {
3260 left[(i * depth) + p] = -by_row[((2 * p) * n) + k1 + i];
3261 left[(i * depth) + width + p] = -by_row[(((2 * p) + 1) * n) + k1 + i];
3264 for (std::size_t p {0}; p < width; ++p) {
3265 for (std::size_t i {0}; i < rest; ++i) {
3266 right[(p * rest) + i] =
conj_of(by_row[(((2 * p) + 1) * n) + k1 + i]);
3267 right[((width + p) * rest) + i] =
conj_of(by_row[((2 * p) * n) + k1 + i]);
3270 T* trailing {a.data() + (k1 * n) + k1};
3271 if constexpr (std::is_same_v<T, double>) {
3273 gemm_triangle<a_rows, b_rows>(rest, depth, left.get(), depth, right.get(), rest, trailing, n,
true);
3279 gemm(rows, r0 + rows, depth, left.get() + (r0 * depth), depth, right.get(), rest, trailing + (r0 * n), n);
3282 for (std::size_t i {0}; i < n; ++i) {
3288 for (std::size_t k {0}; k + 1 < n; ++k) {
3291 return {.reflectors = std::move(reflectors), .delta = std::move(delta)};
3299 template <
typename T>
3304 for (std::size_t i {0}; i < n; ++i) {
3305 mt[(i * n) + i] = T {1};
3307 for (std::size_t k1 {n}; k1 > 1;) {
3308 const std::size_t k0 {k1 - std::min(k1 - 1,
qr_block)};
3312 for (std::size_t j {0}; j < n; ++j) {
3314 for (std::size_t i {0}; i < n; ++i) {
3315 mt[(j * n) + i] *= factor;
3326 template <
typename T>
3331 if constexpr (!std::is_floating_point_v<T>) {
3333 for (std::size_t j {0}; j < n; ++j) {
3338 for (std::size_t k1 {n}; k1 > 1;) {
3339 const std::size_t k0 {k1 - std::min(k1 - 1,
qr_block)};
3373 [[nodiscard]]
inline std::vector<rotation_run>
rotation_runs(
const std::vector<row_operation>& operations)
3375 std::vector<rotation_run> runs;
3376 for (std::size_t t {0}; t < operations.size();) {
3378 std::size_t count {1};
3380 if (op.what == row_operation::kind::rotate && t + 1 < operations.size()
3381 && operations[t + 1].what == row_operation::kind::rotate) {
3382 down = operations[t + 1].
j == op.i;
3383 for (; t + count < operations.size(); ++count) {
3386 const bool chained {down ? next.
j == previous.i : next.i == previous.j};
3387 if (next.what != row_operation::kind::rotate || !chained) {
3392 runs.push_back({.first = t, .count = count, .down = down});
3407 template <
typename T>
3410 const std::vector<row_operation>& operations)
3412 constexpr std::size_t width {64};
3413 const std::vector<rotation_run> runs {
rotation_runs(operations)};
3414 parallel_for((n + width - 1) / width, operations.size() * width, [&](std::size_t chunk) {
3415 const std::size_t first {chunk * width};
3416 const std::size_t count {std::min(first + width, n) - first};
3417 std::array<T, width> carry {};
3420 if (run.count > 1) {
3422 const T* source {m.data() + ((run.down ? start.j : start.i) * n) + first};
3423 std::copy(source, source + count, carry.begin());
3424 for (std::size_t u {run.first}; u < run.first + run.count; ++u) {
3426 const T c {detail::from_double<T>(op.c)};
3427 const T s {detail::from_double<T>(op.s)};
3429 const T* row {m.data() + (op.i * n) + first};
3430 T* other {m.data() + (op.j * n) + first};
3431 for (std::size_t k {0}; k < count; ++k) {
3433 const T b {carry[k]};
3434 other[k] = (c * b) - (s * a);
3435 carry[k] = (c * a) + (s * b);
3439 T* row {m.data() + (op.i * n) + first};
3440 const T* other {m.data() + (op.j * n) + first};
3441 for (std::size_t k {0}; k < count; ++k) {
3442 const T a {carry[k]};
3443 const T b {other[k]};
3444 row[k] = (c * a) + (s * b);
3445 carry[k] = (c * b) - (s * a);
3449 const row_operation& end {operations[run.first + run.count - 1]};
3450 T* target {m.data() + ((run.down ? end.i : end.j) * n) + first};
3451 std::copy(carry.begin(), carry.begin() +
static_cast<std::ptrdiff_t
>(count), target);
3454 T* row {m.data() + (start.i * n) + first};
3455 T* other {m.data() + (start.j * n) + first};
3457 if (start.what == row_operation::kind::rotate) {
3458 const T c {detail::from_double<T>(start.c)};
3459 const T s {detail::from_double<T>(start.s)};
3460 for (std::size_t k {0}; k < count; ++k) {
3462 const T b {other[k]};
3463 row[k] = (c * a) + (s * b);
3464 other[k] = (c * b) - (s * a);
3467 else if (start.what == row_operation::kind::negate) {
3468 for (std::size_t k {0}; k < count; ++k) {
3473 std::swap_ranges(row, row + count, other);
3490 template <
typename T>
3495 std::size_t max_steps)
3497 const double epsilon {std::numeric_limits<double>::epsilon()};
3499 double largest {0.0};
3500 std::size_t steps {0};
3503 std::vector<row_operation> on_zt;
3504 const bool record {!zt.empty()};
3505 for (std::size_t l {0}; l < n; ++l) {
3506 largest = std::max(largest, std::abs(d[l]) + std::abs(e[l]));
3508 while (std::abs(e[m]) > epsilon * largest) {
3511 while (m > l && std::abs(e[l]) > epsilon * largest) {
3512 if (steps == max_steps) {
3513 throw convergence_error(
"symmetric eigendecomposition: QL did not converge within its steps");
3516 const double g0 {d[l]};
3517 double p {(d[l + 1] - g0) / (2.0 * e[l])};
3519 r = p < 0.0 ? -r : r;
3520 d[l] = e[l] / (p + r);
3521 d[l + 1] = e[l] * (p + r);
3522 const double dl1 {d[l + 1]};
3523 const double h0 {g0 - d[l]};
3524 for (std::size_t i {l + 2}; i < n; ++i) {
3532 const double el1 {e[l + 1]};
3535 for (std::size_t i {m}; i-- > l;) {
3539 const double g {c * e[i]};
3540 const double h {c * p};
3545 p = (c * d[i]) - (s * g);
3546 d[i + 1] = h + (s * ((c * g) + (s * d[i])));
3549 on_zt.push_back({row_operation::kind::rotate, i, i + 1, c, -s});
3552 p = -s * s2 * c3 * el1 * e[l] / dl1;
3590 const double root {std::sqrt(std::abs((a * a) - (4.0 * b * c)))};
3591 return a <= 0.0 ? (a - root) / (2.0 * c) : (2.0 * b) / (a + root);
3612 template <
typename Offset>
3616 std::size_t max_iterations,
3618 const Offset& offset)
3620 const std::size_t k {z.size()};
3621 const double rhoinv {1.0 / rho};
3622 const bool last {i + 1 == k};
3623 double weight {0.0};
3624 for (
const double element : z) {
3625 weight += element * element;
3627 std::size_t origin {i};
3629 double upper {2.0 * rho * weight};
3631 const double gap {offset(i, i + 1)};
3633 for (std::size_t j {0}; j < k; ++j) {
3634 f += z[j] * z[j] / (offset(i, j) - (gap / 2.0));
3643 double tau {(lower + upper) / 2.0};
3644 for (std::size_t iteration {0}; iteration < max_iterations; ++iteration) {
3651 for (std::size_t j {0}; j <= i; ++j) {
3652 delta[j] = offset(origin, j) - tau;
3653 const double t {z[j] / delta[j]};
3657 for (std::size_t j {i + 1}; j < k; ++j) {
3658 delta[j] = offset(origin, j) - tau;
3659 const double t {z[j] / delta[j]};
3663 const double w {rhoinv + psi + phi};
3664 if (std::abs(w) <= std::numeric_limits<double>::epsilon()
3665 * ((8.0 * (phi - psi + rhoinv)) + (3.0 * std::abs(tau) * (dpsi + dphi)))) {
3666 return {.
origin = origin, .offset = tau};
3668 (w < 0.0 ? lower : upper) = tau;
3671 const double a1 {delta[i]};
3672 const double a2 {last ? 0.0 : delta[i + 1]};
3673 const double c {w - (a1 * dpsi) - (a2 * dphi)};
3674 const double a {last ? (a1 * c) + (a1 * a1 * dpsi) : ((a1 + a2) * w) - (a1 * a2 * (dpsi + dphi))};
3675 const double b {last ? 0.0 : a1 * a2 * w};
3676 const double next {tau + (last ? a / c :
quadratic_step(a, b, c))};
3679 tau = next > lower && next < upper ? next : (lower + upper) / 2.0;
3699 std::size_t max_iterations,
3703 [&d](std::size_t o, std::size_t j) {
return d[j] - d[o]; })};
3704 return d[root.origin] + root.
offset;
3740 if (
buffer.size < size) {
3741 buffer.data = std::make_unique_for_overwrite<double[]>(size);
3743 double*
const elements {
buffer.data.get()};
3746 std::fill(elements + first, elements + std::min(first +
first_touch_chunk, size), 0.0);
3749 return buffer.data.get();
3800 std::vector<unsigned> reach(n);
3801 for (std::size_t j {0}; j < n; ++j) {
3802 reach[j] = j < split ? 1U : 2U;
3804 for (
double& element : z) {
3805 element /= std::numbers::sqrt2;
3808 std::vector<std::size_t> order(n);
3809 for (std::size_t j {0}; j < n; ++j) {
3812 std::sort(order.begin(), order.end(), [&d](std::size_t x, std::size_t y) { return d[x] < d[y]; });
3813 double largest_d {0.0};
3814 double largest_z {0.0};
3815 for (std::size_t j {0}; j < n; ++j) {
3816 largest_d = std::max(largest_d, std::abs(d[j]));
3817 largest_z = std::max(largest_z, std::abs(z[j]));
3819 const double tolerance {8.0 * std::numeric_limits<double>::epsilon() * std::max(largest_d, largest_z)};
3820 std::vector<std::size_t> kept;
3821 std::vector<std::size_t> deflated;
3822 std::size_t pending {n};
3823 for (
const std::size_t j : order) {
3824 if (rho * std::abs(z[j]) <= tolerance) {
3825 deflated.push_back(j);
3832 const double length {std::hypot(z[j], z[pending])};
3833 const double c {z[j] / length};
3834 const double s {-z[pending] / length};
3835 if (std::abs((d[j] - d[pending]) * c * s) <= tolerance) {
3839 for (std::size_t r {0}; r < n; ++r) {
3840 const double x {q[(r * ldq) + pending]};
3841 const double y {q[(r * ldq) + j]};
3842 q[(r * ldq) + pending] = (c * x) + (s * y);
3843 q[(r * ldq) + j] = (c * y) - (s * x);
3845 reach[j] = reach[pending] = reach[j] | reach[pending];
3846 const double rotated {(d[pending] * c * c) + (d[j] * s * s)};
3847 d[j] = (d[pending] * s * s) + (d[j] * c * c);
3848 d[pending] = rotated;
3849 deflated.push_back(pending);
3852 kept.push_back(pending);
3857 kept.push_back(pending);
3859 const std::size_t k {kept.size()};
3863 for (std::size_t i {0}; i < k; ++i) {
3864 poles[i] = d[kept[i]];
3865 weights[i] = z[kept[i]];
3866 norm += weights[i] * weights[i];
3868 norm = std::sqrt(norm);
3869 for (
double& weight : weights) {
3872 const double scaled_rho {rho * norm * norm};
3885 const std::size_t last {std::min(first +
lowner_block, k)};
3886 std::array<double, lowner_block> product {};
3887 for (std::size_t j {first}; j < last; ++j) {
3888 product[j - first] = gaps[(j * k) + j];
3890 for (std::size_t i {0}; i < k; ++i) {
3891 for (std::size_t j {first}; j < last; ++j) {
3893 product[j - first] *= gaps[(i * k) + j] / (poles[j] - poles[i]);
3897 for (std::size_t j {first}; j < last; ++j) {
3898 signed_weights[j] = std::copysign(std::sqrt(-product[j - first]), weights[j]);
3903 double*
const u {gaps};
3906 const std::size_t ti {tile / tiles};
3907 const std::size_t tj {tile % tiles};
3912 for (std::size_t j {std::max(tj *
lowner_block, ti == tj ? i : 0)}; j < std::min((tj + 1) *
lowner_block, k); ++j) {
3913 const double above {gaps[(i * k) + j]};
3914 const double below {gaps[(j * k) + i]};
3915 u[(j * k) + i] = signed_weights[j] / above;
3916 u[(i * k) + j] = signed_weights[i] / below;
3921 for (std::size_t j {0}; j < k; ++j) {
3922 for (std::size_t i {0}; i < k; ++i) {
3923 length[i] += u[(j * k) + i] * u[(j * k) + i];
3926 for (
double& l : length) {
3930 for (std::size_t i {0}; i < k; ++i) {
3931 u[(j * k) + i] /= length[i];
3937 for (
const auto& [half, first, rows] : {std::tuple {1U, std::size_t {0}, split}, std::tuple {2U, split, n - split}}) {
3938 std::vector<std::size_t> reaching;
3939 for (std::size_t i {0}; i < k; ++i) {
3940 if ((reach[kept[i]] & half) != 0U) {
3941 reaching.push_back(i);
3944 const std::size_t width {reaching.size()};
3948 for (std::size_t c {0}; c < width; ++c) {
3949 columns[(r * width) + c] = q[((first + r) * ldq) + kept[reaching[c]]];
3951 std::fill_n(updated + ((first + r) * k), k, 0.0);
3953 parallel_for(width, k, [&](std::size_t c) { std::copy_n(u + (reaching[c] * k), k, factor + (c * k)); });
3954 gemm(rows, k, width, columns, width, factor, k, updated + (first * k), k);
3959 for (std::size_t i {0}; i < k; ++i) {
3960 values[i] = roots[i];
3962 for (std::size_t i {0}; i < deflated.size(); ++i) {
3963 values[k + i] = d[deflated[i]];
3965 for (std::size_t j {0}; j < n; ++j) {
3968 std::sort(order.begin(), order.end(), [&values](std::size_t x, std::size_t y) { return values[x] < values[y]; });
3969 for (std::size_t j {0}; j < n; ++j) {
3970 d[j] = values[order[j]];
3974 for (std::size_t j {0}; j < n; ++j) {
3975 const std::size_t source {order[j]};
3976 q[(r * ldq) + j] = source < k ? updated[(r * k) + source] : before[deflated[source - k]];
3990 const double* work_e {};
3999 std::size_t depth {};
4000 std::size_t first {};
4001 std::size_t second {};
4020 double largest {0.0};
4021 for (std::size_t i {0}; i < n; ++i) {
4022 largest = std::max({largest, std::abs(d[i]), i + 1 < n ? std::abs(e[i]) : 0.0});
4024 divide_node node {.
d = d, .work_d = d, .work_e = e, .n = n, .q = q, .ldq = ldq, .depth = depth};
4025 (void)std::frexp(largest, &node.exponent);
4026 if (node.exponent != 0) {
4027 node.scaled_d.assign(n,
double {});
4028 node.scaled_e.assign(n, 0.0);
4029 for (std::size_t i {0}; i < n; ++i) {
4030 node.scaled_d[i] = std::ldexp(d[i], -node.exponent);
4031 node.scaled_e[i] = i + 1 < n ? std::ldexp(e[i], -node.exponent) : 0.0;
4033 node.work_d = node.scaled_d.data();
4034 node.work_e = node.scaled_e.data();
4037 nodes.push_back(std::move(node));
4041 node.beta = node.work_e[node.n1 - 1];
4042 node.work_d[node.n1 - 1] -= std::abs(node.beta);
4043 node.work_d[node.n1] -= std::abs(node.beta);
4044 double*
const work_d {node.work_d};
4045 const double*
const work_e {node.work_e};
4046 const std::size_t n1 {node.n1};
4047 const std::size_t index {nodes.size()};
4048 nodes.push_back(std::move(node));
4049 nodes[index].first = nodes.size();
4050 plan_divide(nodes, work_d, work_e, n1, q, ldq, depth + 1);
4051 nodes[index].second = nodes.size();
4052 plan_divide(nodes, work_d + n1, work_e + n1, n - n1, q + (n1 * ldq) + n1, ldq, depth + 1);
4059 const std::size_t n {node.
n};
4062 std::copy(node.
work_e, node.
work_e + (n - 1), sub.begin());
4064 for (std::size_t i {0}; i < n; ++i) {
4065 zt[(i * n) + i] = 1.0;
4068 std::vector<std::size_t> order(n);
4069 for (std::size_t i {0}; i < n; ++i) {
4072 std::sort(order.begin(), order.end(), [&diagonal](std::size_t x, std::size_t y) { return diagonal[x] < diagonal[y]; });
4073 for (std::size_t j {0}; j < n; ++j) {
4074 node.
work_d[j] = diagonal[order[j]];
4075 for (std::size_t r {0}; r < n; ++r) {
4076 node.
q[(r * node.
ldq) + j] = zt[(order[j] * n) + r];
4085 const std::size_t n {node.
n};
4086 const std::size_t n1 {node.
n1};
4087 const std::size_t n2 {n - n1};
4088 double*
const q {node.
q};
4090 for (std::size_t i {0}; i < n1; ++i) {
4091 z[i] = q[((n1 - 1) * node.
ldq) + i];
4093 for (std::size_t i {0}; i < n2; ++i) {
4094 z[n1 + i] = std::copysign(1.0, node.
beta) * q[(n1 * node.
ldq) + n1 + i];
4098 std::copy(values.begin(), values.end(), node.
work_d);
4105 for (std::size_t i {0}; i < node.
n; ++i) {
4149 q.assign(n * n, 0.0);
4150 std::vector<divide_node> nodes;
4152 const auto cut {
static_cast<std::size_t
>(std::bit_width(
max_threads() - 1))};
4153 std::vector<std::size_t> tasks;
4154 for (std::size_t i {0}; i < nodes.size(); ++i) {
4155 if (nodes[i].depth == cut || (nodes[i].depth < cut && nodes[i].n1 == 0)) {
4159 const std::size_t task_order {nodes[tasks[0]].n};
4160 parallel_for(tasks.size(), tasks.size() > 1 ? task_order * task_order * task_order : 0, [&](std::size_t t) {
4164 for (std::size_t level {cut}; level-- > 0;) {
4165 std::vector<std::size_t> joins;
4166 for (std::size_t i {0}; i < nodes.size(); ++i) {
4167 if (nodes[i].n1 != 0 && nodes[i].depth == level) {
4171 if (joins.empty()) {
4174 if (joins.size() == 1) {
4180 const std::size_t order {nodes[joins[0]].n};
4181 parallel_for(joins.size(), order * order * order / 4, [&](std::size_t j) {
4182 join_divide_node(nodes[joins[j]], thread_workspace());
4183 release_large_workspace(thread_workspace());
4184 unscale_divide_node(nodes[joins[j]]);
4190 template <
typename T>
4196 for (std::size_t i {0}; i < n * n; ++i) {
4197 converted[i] = from_double<T>(left[i]);
4200 gemm(n, n, n, converted.data(), n, zt.data(), n, product.data(), n);
4201 zt = std::move(product);
4210 gemm(n, n, n, left.data(), n, zt.data(), n, product.data(), n);
4211 zt = std::move(product);
4216 buffer<std::complex<double>>& zt,
4221 for (std::size_t i {0}; i < n * n; ++i) {
4222 re[i] = zt[i].real();
4223 im[i] = zt[i].imag();
4227 for (std::size_t i {0}; i < n * n; ++i) {
4228 zt[i] = {re[i], im[i]};
4238 template <
typename T>
4253 for (std::size_t r {0}; r < n; ++r) {
4254 at[(j * n) + r] = from_double<T>(q[(r * n) + j]);
4266 for (std::size_t p {0}; p < n; ++p) {
4267 for (std::size_t q {p + 1}; q < n; ++q) {
4268 sum += a[(p * n) + q] * a[(p * n) + q];
4271 return std::sqrt(2.0 *
sum);
4281 const double apq {a[(p * n) + q]};
4285 const double app {a[(p * n) + p]};
4286 const double aqq {a[(q * n) + q]};
4287 const double theta {(aqq - app) / (2.0 * apq)};
4288 const double t {(theta >= 0.0 ? 1.0 : -1.0) / (std::abs(theta) + std::sqrt((theta * theta) + 1.0))};
4289 const double c {1.0 / std::sqrt((t * t) + 1.0)};
4290 const double s {t * c};
4291 a[(p * n) + p] = (c * c * app) - (2.0 * s * c * apq) + (s * s * aqq);
4292 a[(q * n) + q] = (s * s * app) + (2.0 * s * c * apq) + (c * c * aqq);
4293 a[(p * n) + q] = 0.0;
4294 a[(q * n) + p] = 0.0;
4295 for (std::size_t i {0}; i < n; ++i) {
4296 if (i != p && i != q) {
4297 const double aip {a[(i * n) + p]};
4298 const double aiq {a[(i * n) + q]};
4299 a[(i * n) + p] = (c * aip) - (s * aiq);
4300 a[(p * n) + i] = a[(i * n) + p];
4301 a[(i * n) + q] = (s * aip) + (c * aiq);
4302 a[(q * n) + i] = a[(i * n) + q];
4305 for (std::size_t i {0}; i < n; ++i) {
4306 const double vip {v[(i * n) + p]};
4307 const double viq {v[(i * n) + q]};
4308 v[(i * n) + p] = (c * vip) - (s * viq);
4309 v[(i * n) + q] = (s * vip) + (c * viq);
4326 std::size_t max_sweeps)
4330 const double threshold {1e-12 * frobenius};
4332 if (sweeps == max_sweeps) {
4333 throw convergence_error(
"symmetric eigendecomposition: Jacobi did not converge within its sweeps");
4335 for (std::size_t p {0}; p < n; ++p) {
4336 for (std::size_t q {p + 1}; q < n; ++q) {
4346 std::vector<std::size_t> order(diagonal.size());
4347 for (std::size_t i {0}; i < order.size(); ++i) {
4350 std::sort(order.begin(), order.end(), [&diagonal](std::size_t x, std::size_t y) { return diagonal[x] < diagonal[y]; });
4359 template <
typename T>
4361 const std::vector<std::size_t>& order,
4365 parallel_for((n + transpose_tile - 1) / transpose_tile, transpose_tile * n, [&](std::size_t block) {
4366 const std::size_t r0 {block * transpose_tile};
4367 const std::size_t r1 {std::min(r0 + transpose_tile, n)};
4368 for (std::size_t c0 {0}; c0 < n; c0 += transpose_tile) {
4369 for (std::size_t c {c0}; c < std::min(c0 + transpose_tile, n); ++c) {
4370 const T*
const source {zt.data() + (order[c] * n)};
4371 for (std::size_t r {r0}; r < r1; ++r) {
4372 out[(r * n) + c] = source[r];
4413 eigen_method method = eigen_method::divide_and_conquer)
4416 if (input.
cols() != n_) {
4417 throw dimension_error(
"symmetric eigendecomposition requires a square matrix");
4419 detail::require_finite_lower(input,
"symmetric eigendecomposition");
4424 for (std::size_t i {0}; i < n_; ++i) {
4425 for (std::size_t j {0}; j <= i; ++j) {
4426 scale = std::max(scale, std::abs(input(i, j)));
4434 if (method == eigen_method::jacobi) {
4436 double frobenius_squared {0.0};
4437 for (std::size_t i {0}; i < n_; ++i) {
4438 v[(i * n_) + i] = 1.0;
4439 for (std::size_t j {0}; j < n_; ++j) {
4440 a[(i * n_) + j] = input(std::max(i, j), std::min(i, j)) / scale;
4441 frobenius_squared += a[(i * n_) + j] * a[(i * n_) + j];
4444 detail::cyclic_jacobi(a, v, n_, std::sqrt(frobenius_squared), detail::jacobi_sweeps);
4445 for (std::size_t i {0}; i < n_; ++i) {
4446 diagonal[i] = a[(i * n_) + i] * scale;
4448 extract_sorted(diagonal, v);
4451 for (std::size_t i {0}; i < n_; ++i) {
4452 for (std::size_t j {0}; j <= i; ++j) {
4453 a[(i * n_) + j] = input(i, j) / scale;
4460 if (method == eigen_method::tridiagonal_ql) {
4461 zt = detail::tridiagonal_basis(reduction, n_);
4462 detail::tridiagonal_ql(diagonal, off, zt, n_, detail::ql_steps_per_value * n_);
4465 zt = detail::tridiagonal_divide_and_conquer(diagonal, off, reduction, n_);
4468 const std::vector<std::size_t> order {detail::ascending_order(diagonal)};
4470 for (std::size_t c {0}; c < n_; ++c) {
4471 values[c] = diagonal[order[c]] * scale;
4474 vectors_ =
matrix<double> {n_, n_, detail::columns_by_value(zt, order, n_)};
4492 return std::move(vectors_);
4501 std::vector<std::size_t> order(n_);
4502 for (std::size_t i {0}; i < n_; ++i) {
4505 std::sort(order.begin(), order.end(),
4506 [&diagonal](std::size_t x, std::size_t y) {
4507 return diagonal[x] < diagonal[y];
4511 for (std::size_t col {0}; col < n_; ++col) {
4512 values[col] = diagonal[order[col]];
4513 for (std::size_t row {0}; row < n_; ++row) {
4514 vectors[(row * n_) + col] = v[(row * n_) + order[col]];
4552 eigen_method method = eigen_method::divide_and_conquer)
4555 if (method == eigen_method::jacobi) {
4556 throw std::invalid_argument(
"Hermitian eigendecomposition: the Jacobi method is for real symmetric matrices");
4558 if (input.cols() != n_) {
4559 throw dimension_error(
"Hermitian eigendecomposition requires a square matrix");
4561 detail::require_finite_lower(input,
"Hermitian eigendecomposition");
4566 for (std::size_t i {0}; i < n_; ++i) {
4567 for (std::size_t j {0}; j < i; ++j) {
4568 scale = std::max(scale, std::abs(input(i, j)));
4570 scale = std::max(scale, std::abs(input(i, i).real()));
4576 for (std::size_t i {0}; i < n_; ++i) {
4577 for (std::size_t j {0}; j <= i; ++j) {
4578 a[(i * n_) + j] = (i == j ? std::complex<double> {input(i, j).real(), 0.0} : input(i, j)) / scale;
4586 if (method == eigen_method::tridiagonal_ql) {
4587 zt = detail::tridiagonal_basis(reduction, n_);
4588 detail::tridiagonal_ql(diagonal, off, zt, n_, detail::ql_steps_per_value * n_);
4591 zt = detail::tridiagonal_divide_and_conquer(diagonal, off, reduction, n_);
4594 const std::vector<std::size_t> order {detail::ascending_order(diagonal)};
4596 for (std::size_t c {0}; c < n_; ++c) {
4597 values[c] = diagonal[order[c]] * scale;
4618 return std::move(vectors_);
4632 inline constexpr double jacobi_tiny {std::numeric_limits<double>::min() / std::numeric_limits<double>::epsilon()};
4647 template <
typename T>
4651 std::size_t max_sweeps)
4655 const auto transpose_square {[n](
buffer<T>& m) {
4656 for (std::size_t i {0}; i < n; ++i) {
4657 for (std::size_t j {i + 1}; j < n; ++j) {
4658 std::swap(m[(i * n) + j], m[(j * n) + i]);
4662 transpose_square(w);
4663 transpose_square(v);
4665 const double epsilon {
static_cast<double>(std::numeric_limits<value_range_t<T>>::epsilon())};
4666 for (std::size_t sweep {1};; ++sweep) {
4667 bool rotated {
false};
4668 for (std::size_t p {0}; p < n; ++p) {
4669 for (std::size_t q {p + 1}; q < n; ++q) {
4670 const T* wp {w.data() + (p * n)};
4671 const T* wq {w.data() + (q * n)};
4675 for (std::size_t i {0}; i < n; ++i) {
4676 alpha +=
static_cast<double>(std::norm(wp[i]));
4677 beta +=
static_cast<double>(std::norm(wq[i]));
4678 gamma +=
conj_of(wp[i]) * wq[i];
4680 double magnitude {
static_cast<double>(std::abs(gamma))};
4688 if (norm_p == 0.0 || norm_q == 0.0) {
4692 for (std::size_t i {0}; i < n; ++i) {
4693 cosine +=
conj_of(wp[i] / detail::from_double<T>(norm_p)) * (wq[i] / detail::from_double<T>(norm_q));
4695 const double size {
static_cast<double>(std::abs(cosine))};
4696 zeta = ((norm_q / norm_p) - (norm_p / norm_q)) / (2.0 * size);
4697 if (size <= epsilon) {
4704 if (magnitude <= epsilon * std::sqrt(alpha) * std::sqrt(beta)) {
4707 zeta = (beta - alpha) / (2.0 * magnitude);
4711 const double t {(zeta >= 0.0 ? 1.0 : -1.0) * (std::abs(zeta) >
jacobi_large ? 0.5 / std::abs(zeta) : 1.0 / (std::abs(zeta) + std::sqrt(1.0 + (zeta * zeta))))};
4717 const T turn {
conj_of(gamma) / detail::from_double<T>(magnitude)};
4718 const double c {1.0 / std::sqrt(1.0 + (t * t))};
4719 const double s {c * t};
4721 T* mp {side->data() + (p * n)};
4722 T* mq {side->data() + (q * n)};
4723 for (std::size_t i {0}; i < n; ++i) {
4724 const T left {mp[i]};
4725 const T right {turn * mq[i]};
4726 mp[i] = (detail::from_double<T>(c) * left) - (detail::from_double<T>(s) * right);
4727 mq[i] = (detail::from_double<T>(s) * left) + (detail::from_double<T>(c) * right);
4733 transpose_square(w);
4734 transpose_square(v);
4737 if (sweep == max_sweeps) {
4738 throw convergence_error(
"SVD: one-sided Jacobi did not converge within its sweeps");
4769 template <
typename T>
4782 ut.assign(n * n, T {});
4783 vt.assign(n * n, T {});
4784 for (std::size_t k {0}; k < n; ++k) {
4785 ut[(k * n) + k] =
conj_of(left_phase[k]);
4786 vt[(k * n) + k] = k == 0 ? T {1} : right_phase[k - 1];
4788 std::vector<buffer<T>> shifted(n);
4789 for (std::size_t k {0}; k + 1 < n; ++k) {
4790 shifted[k + 1] = std::move(right[k]);
4793 for (std::size_t k {n}; k-- > 0;) {
4799 for (std::size_t k1 {n}; k1 > 0;) {
4811#ifdef NUMERICAL_X86_COMPILED
4812 inline constexpr std::size_t bidiagonal_shared_least {256};
4821#ifdef NUMERICAL_X86_COMPILED
4822 inline constexpr std::size_t bidiagonal_product_weight {4};
4827 template <
typename T>
4828 void bidiagonalize_unblocked(
buffer<T>& b,
4840 template <
typename T>
4845 const T beta {-
phase_of(x[0]) * norm};
4847 const auto length {
static_cast<T
>(
scaled_norm(x))};
4848 for (T& element : x) {
4851 return beta /
static_cast<T
>(up);
4869 template <
typename T>
4870 requires std::is_floating_point_v<T>
4883 std::vector<buffer<T>> left(n);
4884 std::vector<buffer<T>> right(n);
4885 const auto nonzero {[](
const T& value) {
return value != T {}; }};
4894#ifdef NUMERICAL_X86_COMPILED
4899 const std::size_t width {k1 - k0};
4900 std::fill_n(vx.data(), 2 * width * n, T {});
4901 std::fill_n(yu.data(), 2 * width * n, T {});
4902 for (std::size_t r {k0}; r < n; ++r) {
4903 for (std::size_t k {k0}; k < std::min(k1, r + 1); ++k) {
4904 columns[((k - k0) * n) + r] = b[(r * n) + k];
4907 const auto v_row {[&vx, n](std::size_t p, std::size_t from) {
return vx.data() + (2 * p * n) + from; }};
4908 const auto x_row {[&vx, n](std::size_t p, std::size_t from) {
return vx.data() + (((2 * p) + 1) * n) + from; }};
4909 const auto y_row {[&yu, n](std::size_t p, std::size_t from) {
return yu.data() + (2 * p * n) + from; }};
4910 const auto u_row {[&yu, n](std::size_t p, std::size_t from) {
return yu.data() + (((2 * p) + 1) * n) + from; }};
4911#ifdef NUMERICAL_X86_COMPILED
4912 bool column_ahead {
false};
4914 for (std::size_t k {k0}; k < k1; ++k) {
4915 const std::size_t i {k - k0};
4916#ifdef NUMERICAL_X86_COMPILED
4917 const std::size_t from {column_ahead ? i - 1 : 0};
4918 column_ahead =
false;
4920 constexpr std::size_t from {0};
4922 for (std::size_t p {from}; p < i; ++p) {
4923 factors[2 * (p - from)] = *y_row(p, k);
4924 factors[(2 * (p - from)) + 1] = *u_row(p, k);
4926 T*
const column {columns.data() + (i * n) + k};
4927 subtract_products(column, factors.data(), v_row(from, k), n, 2 * (i - from), n - k);
4929 if (std::any_of(x.begin() + 1, x.end(), nonzero)) {
4933 for (std::size_t p {0}; p < i; ++p) {
4934 by_vx[2 * p] =
dot_product(v_row(p, k), x.data(), n - k);
4935 by_vx[(2 * p) + 1] =
dot_product(x_row(p, k), x.data(), n - k);
4937 const std::size_t span {n - k - 1};
4938 constexpr std::size_t chunk_width {64};
4943 std::transform(x.begin(), x.end(), minus.begin(), std::negate<T> {});
4945 const std::size_t first {k + 1 + (chunk * chunk_width)};
4946 const std::size_t count {std::min(chunk_width, n - first)};
4950 for (std::size_t c {0}; c < count; ++c) {
4951 *y_row(i, first + c) =
static_cast<T
>(2) *
sum[c];
4954 std::copy(x.begin(), x.end(), v_row(i, k));
4955 left[k] = std::move(x);
4958 d[k] =
static_cast<double>(x[0]);
4963 buffer<T> y(b.begin() +
static_cast<std::ptrdiff_t
>((k * n) + k + 1),
4964 b.begin() +
static_cast<std::ptrdiff_t
>((k + 1) * n));
4965 for (std::size_t p {0}; p <= i; ++p) {
4966 factors[p] = *v_row(p, k);
4968 subtract_products(y.data(), factors.data(), y_row(0, k + 1), 2 * n, i + 1, n - k - 1);
4969 for (std::size_t p {0}; p < i; ++p) {
4970 factors[p] = *x_row(p, k);
4972 subtract_products(y.data(), factors.data(), u_row(0, k + 1), 2 * n, i, n - k - 1);
4973 if (std::any_of(y.begin() + 1, y.end(), nonzero)) {
4976 const std::size_t rows {n - k - 1};
4980#ifdef NUMERICAL_X86_COMPILED
4981 if (rows >= bidiagonal_shared_least) {
4987 moved[task] =
dot_product(b.data() + ((k + 1 + task) * n) + k + 1, y.data(), y.size());
4989 else if (task - rows <= i) {
4990 by_y[task - rows] =
dot_product(y_row(task - rows, k + 1), y.data(), y.size());
4993 by_u[task - rows - i - 1] =
dot_product(u_row(task - rows - i - 1, k + 1), y.data(), y.size());
4996 T*
const next {k + 1 < k1 && i > 0 ? columns.data() + ((i + 1) * n) + k + 1 :
nullptr};
4997 for (std::size_t p {0}; next !=
nullptr && p < i; ++p) {
4998 next_factors[2 * p] = *y_row(p, k + 1);
4999 next_factors[(2 * p) + 1] = *u_row(p, k + 1);
5001 constexpr std::size_t band {256};
5002 parallel_for((rows + band - 1) / band, band * 4 * (i + 1) * 8, [&](std::size_t c) {
5003 const std::size_t first {c * band};
5004 const std::size_t count {std::min(band, rows - first)};
5005 subtract_products(moved.data() + first, by_y.data(), v_row(0, k + 1 + first), 2 * n, i + 1, count);
5006 subtract_products(moved.data() + first, by_u.data(), x_row(0, k + 1 + first), 2 * n, i, count);
5007 for (std::size_t r {first}; r < first + count; ++r) {
5008 *x_row(i, k + 1 + r) =
static_cast<T
>(2) * moved[r];
5010 if (next !=
nullptr) {
5011 subtract_products(next + first, next_factors.data(), v_row(0, k + 1 + first), n, 2 * i, count);
5014 column_ahead = next !=
nullptr;
5019 for (std::size_t p {0}; p <= i; ++p) {
5020 by_y[p] =
dot_product(y_row(p, k + 1), y.data(), y.size());
5022 for (std::size_t p {0}; p < i; ++p) {
5023 by_u[p] =
dot_product(u_row(p, k + 1), y.data(), y.size());
5026 moved[row] =
dot_product(b.data() + ((k + 1 + row) * n) + k + 1, y.data(), y.size());
5028 subtract_products(moved.data(), by_y.data(), v_row(0, k + 1), 2 * n, i + 1, rows);
5030 for (std::size_t r {0}; r < rows; ++r) {
5031 *x_row(i, k + 1 + r) =
static_cast<T
>(2) * moved[r];
5035 std::copy(y.begin(), y.end(), u_row(i, k + 1));
5036 right[k] = std::move(y);
5039 e[k] =
static_cast<double>(y[0]);
5043 const std::size_t rest {n - k1};
5046 for (std::size_t r {0}; r < rest; ++r) {
5047 for (std::size_t p {0}; p < width; ++p) {
5048 minus_v[(r * width) + p] = -*v_row(p, k1 + r);
5049 minus_x[(r * width) + p] = -*x_row(p, k1 + r);
5052 T* trailing {b.data() + (k1 * n) + k1};
5053 gemm(rest, rest, width, minus_v.data(), width, y_row(0, k1), 2 * n, trailing, n);
5054 gemm(rest, rest, width, minus_x.data(), width, u_row(0, k1), 2 * n, trailing, n);
5077 template <
typename T>
5086 std::vector<buffer<T>> left(n);
5087 std::vector<buffer<T>> right(n);
5090 const auto reflector {[](
buffer<T>& v) {
5093 v[0] +=
phase_of(v[0]) * detail::from_double<T>(norm);
5094 const auto length {detail::from_double<T>(
scaled_norm(v))};
5095 for (T & element : v) {
5099 for (std::size_t k {0}; k < n; ++k) {
5101 for (std::size_t i {k}; i < n; ++i) {
5102 x[i - k] = b[(i * n) + k];
5104 if (
scaled_norm(x.data() + 1, x.size() - 1) > 0.0) {
5107 left[k] = std::move(x);
5110 for (std::size_t j {k}; j < n; ++j) {
5111 b[(k * n) + j] *= left_phase[k];
5113 d[k] =
static_cast<double>(std::abs(b[(k * n) + k]));
5118 for (std::size_t j {k + 1}; j < n; ++j) {
5119 y[j - k - 1] =
conj_of(b[(k * n) + j]);
5121 if (y.size() > 1 &&
scaled_norm(y.data() + 1, y.size() - 1) > 0.0) {
5124 T* row {b.data() + ((k + r) * n) + k + 1};
5125 const T scale {
static_cast<T
>(2) *
dot_product(row, y.data(), y.size())};
5126 for (std::size_t j {0}; j < y.size(); ++j) {
5127 row[j] -= scale *
conj_of(y[j]);
5130 right[k] = std::move(y);
5133 for (std::size_t i {k}; i < n; ++i) {
5134 b[(i * n) + k + 1] *= right_phase[k];
5136 e[k] =
static_cast<double>(std::abs(b[(k * n) + k + 1]));
5142 form_bidiagonal_bases(left, std::move(right), left_phase, right_phase, n, ut, vt);
5147 template <
typename T>
5148 requires(!std::is_floating_point_v<T>)
5173 template <
typename T>
5179 std::size_t max_steps)
5183 std::vector<row_operation> on_vt;
5184 std::vector<row_operation> on_ut;
5185 const bool record {!vt.empty()};
5186 const double epsilon {std::numeric_limits<double>::epsilon()};
5187 const double tiny {std::numeric_limits<double>::min() / epsilon};
5188 constexpr std::size_t none {std::numeric_limits<std::size_t>::max()};
5190 std::size_t steps {0};
5193 std::size_t k {p - 1};
5195 if (std::abs(e[k]) <= tiny + (epsilon * (std::abs(d[k]) + std::abs(d[k + 1])))) {
5200 const std::size_t top {k == none ? 0 : k + 1};
5205 on_vt.push_back({row_operation::kind::negate, top, top, 0.0, 0.0});
5208 for (std::size_t j {top}; j + 1 < n && d[j] < d[j + 1]; ++j) {
5209 std::swap(d[j], d[j + 1]);
5211 on_vt.push_back({row_operation::kind::swap, j, j + 1, 0.0, 0.0});
5212 on_ut.push_back({row_operation::kind::swap, j, j + 1, 0.0, 0.0});
5219 std::size_t zero {none};
5220 for (std::size_t ks {p}; ks-- > top;) {
5221 const double around {std::abs(e[ks]) + (ks != top ? std::abs(e[ks - 1]) : 0.0)};
5222 if (std::abs(d[ks]) <= tiny + (epsilon * around)) {
5228 if (zero == p - 1) {
5229 double f {e[p - 2]};
5231 for (std::size_t j {p - 1}; j-- > top;) {
5233 const double cs {d[j] / t};
5234 const double sn {f / t};
5238 e[j - 1] = cs * e[j - 1];
5241 on_vt.push_back({row_operation::kind::rotate, j, p - 1, cs, sn});
5249 for (std::size_t j {zero + 1}; j < p; ++j) {
5251 const double cs {d[j] / t};
5252 const double sn {f / t};
5257 on_ut.push_back({row_operation::kind::rotate, j, zero, cs, sn});
5262 if (steps == max_steps) {
5263 throw convergence_error(
"SVD: the bidiagonal QR iteration did not converge within its steps");
5268 const double scale {std::max({std::abs(d[p - 1]), std::abs(d[p - 2]), std::abs(e[p - 2]), std::abs(d[top]),
5269 std::abs(e[top])})};
5270 const double sp {d[p - 1] / scale};
5271 const double spm1 {d[p - 2] / scale};
5272 const double epm1 {e[p - 2] / scale};
5273 const double sk {d[top] / scale};
5274 const double ek {e[top] / scale};
5275 const double bb {(((spm1 + sp) * (spm1 - sp)) + (epm1 * epm1)) / 2.0};
5276 const double cc {(sp * epm1) * (sp * epm1)};
5278 if (bb != 0.0 || cc != 0.0) {
5279 shift = std::sqrt((bb * bb) + cc);
5280 shift = cc / (bb < 0.0 ? bb - shift : bb + shift);
5282 double f {((sk + sp) * (sk - sp)) + shift};
5284 for (std::size_t j {top}; j + 1 < p; ++j) {
5291 f = (cs * d[j]) + (sn * e[j]);
5292 e[j] = (cs * e[j]) - (sn * d[j]);
5294 d[j + 1] = cs * d[j + 1];
5296 on_vt.push_back({row_operation::kind::rotate, j, j + 1, cs, sn});
5302 f = (cs * e[j]) + (sn * d[j + 1]);
5303 d[j + 1] = (cs * d[j + 1]) - (sn * e[j]);
5305 e[j + 1] = cs * e[j + 1];
5307 on_ut.push_back({row_operation::kind::rotate, j, j + 1, cs, sn});
5319 template <
typename T>
5323 for (std::size_t i {0}; i < a.
rows(); ++i) {
5324 for (std::size_t j {0}; j < a.
cols(); ++j) {
5348 std::size_t max_iterations,
5354 return (d[j] - d[o]) * (d[j] + d[o]);
5356 const double pole {d[root.origin]};
5357 const double shift {root.
offset / (pole + std::sqrt((pole * pole) + root.offset))};
5358 for (std::size_t j {0}; j < d.size(); ++j) {
5359 below[j] = (d[j] - pole) - shift;
5360 above[j] = (d[j] + pole) + shift;
5362 return pole + shift;
5373 for (std::size_t r {0}; r < m.size() / cols; ++r) {
5374 const double x {m[(r * cols) + a]};
5375 const double y {m[(r * cols) + b]};
5376 m[(r * cols) + a] = (c * x) + (s * y);
5377 m[(r * cols) + b] = (c * y) - (s * x);
5461 std::vector<unsigned> reach_u(n, 4U);
5462 std::vector<unsigned> reach_v(m, 4U);
5464 reach_v[0] = m > n ? 5U : 1U;
5465 for (std::size_t j {1}; j <= split; ++j) {
5469 const auto turned {[](std::vector<unsigned>& reach, std::size_t a, std::size_t b) {
5470 reach[a] = reach[b] = reach[a] | reach[b];
5472 std::vector<std::size_t> order(n - 1);
5473 for (std::size_t j {1}; j < n; ++j) {
5476 std::sort(order.begin(), order.end(), [&d](std::size_t x, std::size_t y) { return d[x] < d[y]; });
5477 double largest {std::max(std::abs(alpha), std::abs(beta))};
5478 for (
const double value : d) {
5479 largest = std::max(largest, value);
5481 if (largest == 0.0) {
5484 const double tolerance {8.0 * std::numeric_limits<double>::epsilon() * largest};
5485 if (std::abs(z[0]) <= tolerance) {
5488 std::vector<std::size_t> kept {0};
5489 std::vector<std::size_t> deflated;
5490 std::size_t pending {n};
5491 for (
const std::size_t j : order) {
5492 if (std::abs(z[j]) <= tolerance) {
5493 deflated.push_back(j);
5496 if (d[j] <= tolerance) {
5498 const double length {std::hypot(z[0], z[j])};
5500 turned(reach_v, 0, j);
5503 deflated.push_back(j);
5506 if (pending != n && d[j] - d[pending] <= tolerance) {
5508 const double length {std::hypot(z[j], z[pending])};
5509 const double c {z[j] / length};
5510 const double s {z[pending] / length};
5513 turned(reach_v, j, pending);
5514 turned(reach_u, j, pending);
5517 deflated.push_back(pending);
5519 else if (pending != n) {
5520 kept.push_back(pending);
5525 kept.push_back(pending);
5527 const std::size_t k {kept.size()};
5530 for (std::size_t i {0}; i < k; ++i) {
5531 poles[i] = d[kept[i]];
5532 weights[i] = z[kept[i]];
5547 const std::size_t last {std::min(first +
lowner_block, k)};
5548 std::array<double, lowner_block> product {};
5549 for (std::size_t j {first}; j < last; ++j) {
5550 product[j - first] = -below[(j * k) + j] * above[(j * k) + j];
5552 for (std::size_t i {0}; i < k; ++i) {
5553 for (std::size_t j {first}; j < last; ++j) {
5555 product[j - first] *= (-below[(i * k) + j] * above[(i * k) + j]) / ((poles[i] - poles[j]) * (poles[i] + poles[j]));
5559 for (std::size_t j {first}; j < last; ++j) {
5560 signed_weights[j] = std::copysign(std::sqrt(product[j - first]), weights[j]);
5565 for (std::size_t i {0}; i < k; ++i) {
5566 const double ratio {signed_weights[j] / (below[(i * k) + j] * above[(i * k) + j])};
5567 right[(j * k) + i] = ratio;
5568 left[(j * k) + i] = j == 0 ? -1.0 : poles[j] * ratio;
5571 for (
double*
const vectors : {left, right}) {
5573 for (std::size_t j {0}; j < k; ++j) {
5574 for (std::size_t i {0}; i < k; ++i) {
5575 length[i] += vectors[(j * k) + i] * vectors[(j * k) + i];
5578 for (
double& l : length) {
5582 for (std::size_t i {0}; i < k; ++i) {
5583 vectors[(j * k) + i] /= length[i];
5590 using segment = std::tuple<std::size_t, std::size_t, unsigned>;
5591 const auto carried {[&](
const buffer<double>& basis, std::size_t rows,
const double* small,
5592 const std::vector<unsigned>& reach,
const std::vector<segment>& segments,
double* product) {
5593 for (
const auto& [first, count, bit] : segments) {
5594 std::vector<std::size_t> reaching;
5595 for (std::size_t i {0}; i < k; ++i) {
5596 if ((reach[kept[i]] & bit) != 0U) {
5597 reaching.push_back(i);
5600 const std::size_t width {reaching.size()};
5604 for (std::size_t c {0}; c < width; ++c) {
5605 columns[(r * width) + c] = basis[((first + r) * rows) + kept[reaching[c]]];
5607 std::fill_n(product + ((first + r) * k), k, 0.0);
5609 for (std::size_t c {0}; c < width; ++c) {
5610 std::copy_n(small + (reaching[c] * k), k, factor + (c * k));
5612 gemm(count, k, width, columns, width, factor, k, product + (first * k), k);
5617 carried(u, n, left, reach_u, {{0, split, 1U}, {split, 1, 2U}, {split + 1, n - split - 1, 4U}}, new_u);
5618 carried(v, m, right, reach_v, {{0, split + 1, 1U}, {split + 1, m - split - 1, 4U}}, new_v);
5620 std::vector<std::size_t> from(n);
5621 for (std::size_t i {0}; i < k; ++i) {
5622 values[i] = roots[i];
5625 for (std::size_t i {0}; i < deflated.size(); ++i) {
5626 values[k + i] = d[deflated[i]];
5627 from[k + i] = k + i;
5629 std::vector<std::size_t> rank(n);
5630 for (std::size_t j {0}; j < n; ++j) {
5633 std::sort(rank.begin(), rank.end(), [&values](std::size_t x, std::size_t y) { return values[x] < values[y]; });
5634 std::vector<std::size_t> source(n);
5635 for (std::size_t c {0}; c < n; ++c) {
5636 source[c] = from[rank[c]];
5637 d[c] = values[rank[c]];
5642 sorted_u.resize(n * n);
5643 sorted_v.assign(v.begin(), v.end());
5645 for (std::size_t c {0}; c < n; ++c) {
5647 sorted_u[(r * n) + c] = source[c] < k ? new_u[(r * k) + source[c]] : u[(r * n) + deflated[source[c] - k]];
5649 sorted_v[(r * m) + c] = source[c] < k ? new_v[(r * k) + source[c]] : v[(r * m) + deflated[source[c] - k]];
5652 std::swap(u, sorted_u);
5653 std::swap(v, sorted_v);
5668 std::size_t extra {};
5673 std::size_t first {};
5674 std::size_t second {};
5675 std::size_t depth {};
5694 double largest {0.0};
5695 for (std::size_t i {0}; i < n; ++i) {
5696 largest = std::max({largest, std::abs(d[i]), i + 1 < n + extra ? std::abs(e[i]) : 0.0});
5698 bidiagonal_node node {.
d = d, .e = e, .n = n, .extra = extra, .depth = depth};
5699 (void)std::frexp(largest, &node.exponent);
5700 if (node.exponent != 0) {
5701 node.scaled_d.assign(n,
double {});
5702 node.scaled_e.assign(n, 0.0);
5703 for (std::size_t i {0}; i < n; ++i) {
5704 node.scaled_d[i] = std::ldexp(d[i], -node.exponent);
5705 node.scaled_e[i] = i + 1 < n + extra ? std::ldexp(e[i], -node.exponent) : 0.0;
5707 node.d = node.scaled_d.data();
5708 node.e = node.scaled_e.data();
5710 const std::size_t index {nodes.size()};
5712 nodes.push_back(std::move(node));
5716 const double*
const work_d {node.d};
5717 const double*
const work_e {node.e};
5718 const std::size_t k {node.k};
5719 nodes.push_back(std::move(node));
5720 const std::size_t first {
plan_bidiagonal(nodes, work_d, work_e, k, 1, depth + 1)};
5721 const std::size_t second {
plan_bidiagonal(nodes, work_d + k + 1, work_e + k + 1, n - k - 1, extra, depth + 1)};
5722 nodes[index].first = first;
5723 nodes[index].second = second;
5730 const std::size_t m {node.
n + node.
extra};
5731 node.
sigma.assign(node.
n, 0.0);
5732 node.
u.assign(node.
n * node.
n, 0.0);
5733 node.
v.assign(m * m, 0.0);
5734 for (std::size_t i {0}; i < m; ++i) {
5735 node.
v[(i * m) + i] = 1.0;
5743 const double*
const d {node.
d};
5744 const double*
const e {node.
e};
5745 const std::size_t n {node.
n};
5746 const std::size_t extra {node.
extra};
5747 const std::size_t m {n + extra};
5753 std::copy_n(e, n - std::min<std::size_t>(n, 1), super.begin());
5756 double fill {extra == 1 ? e[n - 1] : 0.0};
5757 for (std::size_t j {n}; extra == 1 && j-- > 0;) {
5758 const double length {std::hypot(diagonal[j], fill)};
5759 const double c {length == 0.0 ? 1.0 : diagonal[j] / length};
5760 const double s {length == 0.0 ? 0.0 : fill / length};
5761 diagonal[j] = length;
5764 fill = -s * super[j - 1];
5765 super[j - 1] = c * super[j - 1];
5770 for (std::size_t i {0}; i < n; ++i) {
5771 ut[(i * n) + i] = 1.0;
5772 vt[(i * n) + i] = 1.0;
5776 for (std::size_t c {0}; c < n; ++c) {
5777 const std::size_t source {n - 1 - c};
5778 sigma[c] = diagonal[source];
5779 for (std::size_t r {0}; r < n; ++r) {
5780 u[(r * n) + c] = ut[(source * n) + r];
5782 for (std::size_t r {0}; r < m; ++r) {
5784 for (std::size_t t {0}; t < n; ++t) {
5785 sum += rotated[(r * m) + t] * vt[(source * n) + t];
5787 v[(r * m) + c] =
sum;
5799 const std::size_t n {node.
n};
5800 const std::size_t extra {node.
extra};
5801 const std::size_t m {n + extra};
5802 const std::size_t k {node.
k};
5803 const std::size_t lower {n - k - 1};
5804 const double alpha {node.
d[k]};
5805 const double beta {node.
e[k]};
5814 const std::size_t m1 {k + 1};
5815 const std::size_t m2 {lower + extra};
5820 for (std::size_t c {0}; c < k; ++c) {
5821 arrow[1 + c] = sigma1[c];
5822 z[1 + c] = alpha * v1[(k * m1) + c];
5823 for (std::size_t r {0}; r < k; ++r) {
5824 u[(r * n) + 1 + c] = u1[(r * k) + c];
5827 for (std::size_t c {0}; c < lower; ++c) {
5828 arrow[k + 1 + c] = sigma2[c];
5829 z[k + 1 + c] = beta * v2[c];
5830 for (std::size_t r {0}; r < lower; ++r) {
5831 u[((k + 1 + r) * n) + k + 1 + c] = u2[(r * lower) + c];
5834 std::fill(v.begin(), v.end(), 0.0);
5835 for (std::size_t r {0}; r < m1; ++r) {
5836 v[(r * m) + 0] = v1[(r * m1) + k];
5837 for (std::size_t c {0}; c < k; ++c) {
5838 v[(r * m) + 1 + c] = v1[(r * m1) + c];
5841 for (std::size_t r {0}; r < m2; ++r) {
5842 for (std::size_t c {0}; c < lower; ++c) {
5843 v[((m1 + r) * m) + k + 1 + c] = v2[(r * m2) + c];
5846 z[0] = alpha * v1[(k * m1) + k];
5848 for (std::size_t r {0}; r < m2; ++r) {
5849 v[((m1 + r) * m) + n] = v2[(r * m2) + lower];
5851 const double other {beta * v2[lower]};
5852 const double length {std::hypot(z[0], other)};
5853 const double c {length == 0.0 ? 1.0 : z[0] / length};
5854 const double s {length == 0.0 ? 0.0 : other / length};
5858 merge_arrow(arrow, std::move(z), alpha, beta, u, v, n, m, k, work);
5859 node.
sigma = std::move(arrow);
5866 for (
double& value : node.
sigma) {
5867 value = std::ldexp(value, node.
exponent);
5889 if (nodes[index].k == 0) {
5926 std::vector<bidiagonal_node> nodes;
5928 const auto cut {
static_cast<std::size_t
>(std::bit_width(
max_threads() - 1))};
5929 std::vector<std::size_t> tasks;
5930 for (std::size_t i {0}; i < nodes.size(); ++i) {
5931 if (nodes[i].depth == cut || (nodes[i].depth < cut && nodes[i].k == 0)) {
5935 const std::size_t task_order {nodes[tasks[0]].n};
5936 parallel_for(tasks.size(), tasks.size() > 1 ? task_order * task_order * task_order : 0, [&](std::size_t t) {
5940 for (std::size_t level {cut}; level-- > 0;) {
5941 std::vector<std::size_t> joins;
5942 for (std::size_t i {0}; i < nodes.size(); ++i) {
5943 if (nodes[i].k != 0 && nodes[i].depth == level) {
5947 const std::size_t order {joins.empty() ? 0 : nodes[joins[0]].n};
5948 parallel_for(joins.size(), joins.size() > 1 ? order * order * order / 4 : 0, [&](std::size_t j) {
5954 sigma = std::move(nodes[0].sigma);
5955 u = std::move(nodes[0].u);
5956 v = std::move(nodes[0].v);
5965 template <
typename T>
5981 for (std::size_t c {0}; c < n; ++c) {
5982 d[c] = sigma[n - 1 - c];
5983 for (std::size_t r {0}; r < n; ++r) {
5984 left[(c * n) + r] = u[(r * n) + (n - 1 - c)];
5985 right[(c * n) + r] = v[(r * n) + (n - 1 - c)];
6016 template <
typename T>
6030 svd_method method = svd_method::divide_and_conquer,
6033 detail::require_finite(a,
"SVD");
6035 factor(a, method,
full);
6038 factor(detail::adjoint(a), method,
full);
6046 return singular_values_;
6058 return std::move(u_);
6071 return detail::adjoint(v_);
6086 if (width == cols) {
6087 return matrix<T> {rows, cols, std::move(q)} *
matrix<T> {cols, cols, std::move(left)};
6090 detail::gemm(rows, cols, cols, q.data(), rows, left.data(), cols, u.data(), rows);
6091 for (std::size_t i {0}; i < rows; ++i) {
6092 for (std::size_t j {cols}; j < rows; ++j) {
6093 u[(i * rows) + j] = q[(i * rows) + j];
6096 return matrix<T> {rows, rows, std::move(u)};
6105 const std::size_t rows {a.
rows()};
6106 const std::size_t cols {a.
cols()};
6111 const T*
const source {std::to_address(a.
begin())};
6112 const std::size_t bands {(rows + detail::svd_band - 1) / detail::svd_band};
6113 std::vector<double> largest(bands, 0.0);
6114 detail::parallel_for(bands, detail::memory_element_weight * detail::svd_band * cols, [source, rows, cols, &largest](std::size_t band) {
6116 for (std::size_t e {band * detail::svd_band * cols}; e < std::min(rows, (band + 1) * detail::svd_band) * cols; ++e) {
6117 most = std::max(most,
static_cast<double>(std::abs(source[e])));
6119 largest[band] = most;
6121 double scale {*std::max_element(largest.begin(), largest.end())};
6126 T*
const into {entries.data()};
6127 const T divisor {detail::from_double<T>(scale)};
6128 detail::parallel_for(bands, detail::memory_element_weight * detail::svd_band * cols, [source, into, rows, cols, divisor](std::size_t band) {
6129 for (std::size_t e {band * detail::svd_band * cols}; e < std::min(rows, (band + 1) * detail::svd_band) * cols; ++e) {
6130 into[e] = source[e] / divisor;
6135 const bool direct {rows == cols && method != svd_method::jacobi};
6136 const std::size_t width {
full ? rows : cols};
6138 : detail::householder_qr(std::move(entries), rows, cols,
full)};
6139 if (method != svd_method::jacobi) {
6144 detail::bidiagonalize(factors.r, cols, d, e, ut, vt);
6145 if (method == svd_method::bidiagonal_qr) {
6146 detail::bidiagonal_qr(d, e, ut, vt, cols, detail::svd_qr_steps_per_value * cols);
6149 detail::bidiagonal_divide_and_conquer(d, e, ut, vt, cols);
6153 detail::transpose_into(ut.data(), cols, cols, left.data());
6154 detail::transpose_into(vt.data(), cols, cols, right.data());
6155 for (
double& value : d) {
6159 u_ = direct ?
matrix<T> {cols, cols, std::move(left)}
6160 : left_vectors(std::move(factors.q), std::move(left), rows, cols, width);
6161 v_ =
matrix<T> {cols, cols, std::move(right)};
6166 for (std::size_t i {0}; i < cols; ++i) {
6167 right[(i * cols) + i] = T {1};
6169 detail::one_sided_jacobi(w, right, cols, detail::jacobi_sweeps);
6172 for (std::size_t j {0}; j < cols; ++j) {
6174 for (std::size_t i {0}; i < cols; ++i) {
6175 column[i] = w[(i * cols) + j];
6177 norms[j] = detail::scaled_norm(column);
6179 std::vector<std::size_t> order(cols);
6180 for (std::size_t j {0}; j < cols; ++j) {
6183 std::stable_sort(order.begin(), order.end(), [&norms](std::size_t x, std::size_t y) { return norms[x] > norms[y]; });
6189 buffer<T> sorted_right(cols * cols, T {});
6191 for (std::size_t d {0}; d < cols; ++d) {
6192 const std::size_t j {order[d]};
6193 values[d] = norms[j] * scale;
6194 for (std::size_t i {0}; i < cols; ++i) {
6195 sorted_right[(i * cols) + d] = right[(i * cols) + j];
6197 if (norms[j] > 0.0) {
6198 for (std::size_t i {0}; i < cols; ++i) {
6199 left[(i * cols) + d] = w[(i * cols) + j] / detail::from_double<T>(norms[j]);
6204 double best_norm {-1.0};
6205 for (std::size_t k {0}; k < cols; ++k) {
6207 candidate[k] = T {1};
6208 orthogonalise(candidate, left, cols, d);
6209 const double norm {detail::scaled_norm(candidate)};
6210 if (norm > best_norm) {
6211 best = std::move(candidate);
6215 orthogonalise(best, left, cols, d);
6216 const double norm {detail::scaled_norm(best)};
6217 for (std::size_t i {0}; i < cols; ++i) {
6218 left[(i * cols) + d] = best[i] / detail::from_double<T>(norm);
6222 u_ = left_vectors(std::move(factors.q), std::move(left), rows, cols, width);
6223 v_ =
matrix<T> {cols, cols, std::move(sorted_right)};
6232 for (std::size_t c {0}; c < count; ++c) {
6234 for (std::size_t i {0}; i < n; ++i) {
6235 projection += detail::conj_of(basis[(i * n) + c]) * x[i];
6237 for (std::size_t i {0}; i < n; ++i) {
6238 x[i] -= projection * basis[(i * n) + c];
6257 double largest {0.0};
6258 for (std::size_t d {0}; d < sigma.
size(); ++d) {
6259 largest = std::max(largest, sigma[d]);
6261 const double tolerance {cutoff.magnitude(largest, rows, cols)};
6262 std::size_t rank {0};
6263 for (std::size_t d {0}; d < sigma.
size(); ++d) {
6264 rank +=
static_cast<std::size_t
>(sigma[d] > tolerance);
6275 template <
typename T>
6279 return std::scalbn(x, exponent);
6283 template <
typename U>
6287 return std::complex<U> {std::scalbn(x.real(), exponent), std::scalbn(x.imag(), exponent)};
6295 template <
typename T>
6299 constexpr std::size_t width {64};
6301 parallel_for((n + width - 1) / width, n * n * width / 2, [&](std::size_t chunk) {
6302 const std::size_t first {chunk * width};
6303 const std::size_t last {std::min(first + width, n)};
6304 for (std::size_t i {last}; i-- > 0;) {
6305 T*
const row {x.data() + (i * n) + first};
6307 row[i - first] = T {1};
6309 subtract_products(row, r.data() + (i * n) + i + 1, x.data() + ((i + 1) * n) + first, n, last - (i + 1), last - first);
6310 for (std::size_t c {0}; c < last - first; ++c) {
6311 row[c] = row[c] / r[(i * n) + i];
6333 template <
typename T>
6337 std::pair<
buffer<T>,
int>*
const r =
nullptr)
6339 const std::size_t m {a.
rows()};
6340 const std::size_t n {a.
cols()};
6341 const bool wide {m < n};
6342 const std::size_t rows {wide ? n : m};
6343 const std::size_t cols {wide ? m : n};
6344 double largest {0.0};
6345 for (std::size_t i {0}; i < m; ++i) {
6346 for (std::size_t j {0}; j < n; ++j) {
6347 largest = std::max(largest,
static_cast<double>(std::abs(a(i, j))));
6350 if (largest == 0.0) {
6351 return std::nullopt;
6353 const int exponent {std::ilogb(largest)};
6355 for (std::size_t i {0}; i < m; ++i) {
6356 for (std::size_t j {0}; j < n; ++j) {
6361 entries[(i * cols) + j] =
scaled_by_two(a(i, j), -exponent);
6367 double r_squares {0.0};
6368 double inverse_squares {0.0};
6369 for (std::size_t e {0}; e < cols * cols; ++e) {
6370 r_squares +=
static_cast<double>(std::norm(factors.r[e]));
6371 inverse_squares +=
static_cast<double>(std::norm(
inverse[e]));
6373 const double smallest_bound {1.0 / std::sqrt(inverse_squares)};
6375 return std::nullopt;
6377 const std::size_t k {b.
cols()};
6382 gemm(cols, k, cols, &*inverse_adjoint.begin(), cols, right.data(), k, x.data(), k);
6383 for (std::size_t block {factors.blocks.size()}; block-- > 0;) {
6392 gemm(n, k, cols,
inverse.data(), cols, y.data(), k, x.data(), k);
6394 for (T& value : x) {
6398 *r = {std::move(factors.r), exponent};
6408 template <
typename T>
6425 template <
typename T>
6431 const std::size_t rows {s.
u().rows()};
6432 const std::size_t cols {s.
v().rows()};
6434 for (std::size_t c {0}; c < b.
cols(); ++c) {
6435 for (std::size_t d {0}; d < rank; ++d) {
6437 for (std::size_t i {0}; i < rows; ++i) {
6438 coefficient +=
conj_of(s.
u()(i, d)) * b(i, c);
6440 coefficient /= from_double<T>(sigma[d]);
6441 for (std::size_t j {0}; j < cols; ++j) {
6442 x[(j * b.
cols()) + c] += coefficient * s.
v()(j, d);
6464 template <
typename T>
6469 const matrix<T> column {lstsq(a, matrix<T> {b.
size(), 1, buffer<T>(b.
begin(), b.
end())}, cutoff)};
6470 return vector<T> {buffer<T>(column.begin(), column.end())};
6486 template <
typename T>
6492 throw dimension_error(
"lstsq: right-hand side size differs from the matrix");
6494 detail::require_finite(a,
"lstsq");
6495 if (std::optional<matrix<T>> fast {detail::lstsq_by_qr(a, b, cutoff)}) {
6496 return *std::move(fast);
6498 const svd_decomposition<T> s {a};
6499 return detail::lstsq_from_svd(s, detail::numerical_rank(s.singular_values(), a.
rows(), a.
cols(), cutoff), b);
6510 template <
typename T>
6513 detail::require_finite(a,
"singular values");
6514 const bool tall {a.
rows() >= a.
cols()};
6515 const std::size_t rows {tall ? a.
rows() : a.
cols()};
6516 const std::size_t cols {tall ? a.
cols() : a.
rows()};
6519 for (std::size_t i {0}; i < a.
rows(); ++i) {
6520 for (std::size_t j {0}; j < a.
cols(); ++j) {
6521 scale = std::max(scale,
static_cast<double>(std::abs(a(i, j))));
6524 scale = scale == 0.0 ? 1.0 : scale;
6526 for (std::size_t i {0}; i < rows; ++i) {
6527 for (std::size_t j {0}; j < cols; ++j) {
6528 entries[(i * cols) + j] = (tall ? a(i, j) : detail::conj_of(a(j, i))) / detail::from_double<T>(scale);
6531 buffer<T> r {rows == cols ? std::move(entries) : detail::householder_reflect(std::move(entries), rows, cols).r};
6532 buffer<double> d {detail::square_singular_values(std::move(r), cols)};
6533 for (
double& value : d) {
6548 template <
typename T>
6551 const std::size_t n {a.
rows()};
6552 if (a.
cols() != n) {
6553 throw dimension_error(
"symmetric eigenvalues require a square matrix");
6555 detail::require_finite_lower(a,
"symmetric eigenvalues");
6558 for (std::size_t i {0}; i < n; ++i) {
6559 for (std::size_t j {0}; j < i; ++j) {
6560 scale = std::max(scale,
static_cast<double>(std::abs(a(i, j))));
6562 scale = std::max(scale, std::abs(detail::real_part(a(i, i))));
6564 scale = scale == 0.0 ? 1.0 : scale;
6566 for (std::size_t i {0}; i < n; ++i) {
6567 for (std::size_t j {0}; j < i; ++j) {
6568 entries[(i * n) + j] = a(i, j) / detail::from_double<T>(scale);
6570 entries[(i * n) + i] = detail::from_double<T>(detail::real_part(a(i, i)) / scale);
6576 static_cast<void>(detail::tridiagonalize(entries, n, d, off));
6577 detail::tridiagonal_ql(d, off, none, n, detail::ql_steps_per_value * n);
6579 std::sort(d.begin(), d.end());
6580 for (
double& value : d) {
6587 template <
typename T>
6592 std::size_t rank {0};
6606 template <
typename T>
6612 throw dimension_error(
"lstsq: right-hand side size differs from the matrix");
6614 detail::require_finite(a,
"lstsq");
6616 std::pair<buffer<T>,
int> r;
6617 std::optional<matrix<T>> fast {detail::lstsq_by_qr(a, b, cutoff, &r)};
6618 std::optional<svd_decomposition<T>> s;
6622 buffer<double> values {fast ? detail::square_singular_values(std::move(r.first), std::min(a.
rows(), a.
cols()))
6625 for (
double& value : values) {
6626 value = std::ldexp(value, r.second);
6629 const vector<double> sigma {std::move(values)};
6630 const std::size_t rank {detail::numerical_rank(sigma, a.
rows(), a.
cols(), cutoff)};
6631 matrix<T> x {fast ? *std::move(fast) : detail::
lstsq_from_svd(*s, rank, b)};
6632 buffer<double> residuals;
6634 const matrix<T> fitted {a * x};
6635 residuals.assign(b.
cols(), 0.0);
6636 for (std::size_t i {0}; i < b.
rows(); ++i) {
6637 for (std::size_t c {0}; c < b.
cols(); ++c) {
6638 residuals[c] += std::norm(b(i, c) - fitted(i, c));
6642 return least_squares_solution<T> {.solution = std::move(x),
6643 .residuals = vector<double> {std::move(residuals)},
6645 .singular_values = sigma};
6652 template <
typename T>
6655 const std::size_t n {a.
rows()};
6657 for (std::size_t i {0}; i < n; ++i) {
6658 for (std::size_t j {0}; j < i; ++j) {
6659 entries[(i * n) + j] = a(i, j);
6660 entries[(j * n) + i] =
conj_of(a(i, j));
6662 entries[(i * n) + i] = from_double<T>(
real_part(a(i, i)));
6664 return matrix<T> {n, n, std::move(entries)};
6674 template <
typename T>
6680 const std::size_t rows {left.
rows()};
6681 const std::size_t cols {right.
rows()};
6682 double largest {0.0};
6683 for (std::size_t d {0}; d < sigma.
size(); ++d) {
6684 largest = std::max(largest, std::abs(sigma[d]));
6686 const double tolerance {cutoff.
magnitude(largest, rows, cols)};
6687 std::vector<std::size_t> kept;
6688 for (std::size_t d {0}; d < sigma.
size(); ++d) {
6689 if (std::abs(sigma[d]) > tolerance) {
6693 const std::size_t rank {kept.size()};
6695 buffer<T> left_adjoint(rank * rows, T {});
6696 for (std::size_t e {0}; e < rank; ++e) {
6697 const std::size_t d {kept[e]};
6698 const auto inverse {from_double<T>(1.0 / sigma[d])};
6699 for (std::size_t i {0}; i < cols; ++i) {
6700 scaled[(i * rank) + e] = right(i, d) *
inverse;
6702 for (std::size_t j {0}; j < rows; ++j) {
6703 left_adjoint[(e * rows) + j] =
conj_of(left(j, d));
6707 gemm(cols, rows, rank, scaled.data(), rank, left_adjoint.data(), rows, out.data(), rows);
6708 return matrix<T> {cols, rows, std::move(out)};
6715 template <
typename T>
6723 template <
typename T>
6727 buffer<double> values(signed_values.begin(), signed_values.end());
6728 for (
double& value : values) {
6729 value = std::abs(value);
6750 template <
typename T>
6764 return inverse_on_kept(decomposition.values(), vectors, vectors, cutoff);
6773 return inverse_on_kept(decomposition.values(), vectors, vectors, cutoff);
6788 template <
typename T>
6791 bool hermitian =
false)
6795 throw dimension_error(
"matrix_rank: a Hermitian matrix is square");
6797 detail::require_finite_lower(a,
"matrix_rank");
6798 return detail::numerical_rank(detail::hermitian_singular_values(a), a.
rows(), a.
cols(), cutoff);
6814 template <
typename T>
6817 bool hermitian =
false)
6821 throw dimension_error(
"pinv: a Hermitian matrix is square");
6823 detail::require_finite_lower(a,
"pinv");
6824 return detail::hermitian_pseudo_inverse(a, cutoff);
6826 detail::require_finite(a,
"pinv");
6827 const svd_decomposition<T> s {a};
6828 return detail::inverse_on_kept(s.singular_values(), s.v(), s.u(), cutoff);
A Cholesky factorisation A = L Lᴴ of a Hermitian positive-definite matrix.
cholesky_decomposition(matrix< T > &&a)
Factorises a in its own storage, an expiring matrix's elements taken over: no copy of them.
vector< T > solve(const vector< T > &b) const
Solves A x = b via L y = b then Lᴴ x = y.
std::size_t n_
The matrix order.
cholesky_decomposition(const matrix< T > &a)
Factorises a.
matrix< T > lower() &&
The lower-triangular factor L, moved out of an expiring decomposition.
void factor_rows_together(std::size_t i, std::size_t k0)
The columns [k0, i) of the detail::cholesky_diagonal_rows rows from i, together: each element depends...
void factor_row(std::size_t i, std::size_t k0, std::size_t first, std::size_t last)
Row i of L in the columns [first, last), the terms of the columns before k0 already subtracted: the u...
T determinant() const
The determinant — the product of the squared diagonal of L (real, positive).
void factor_rows_below(std::size_t k0, std::size_t end, buffer< T > &minus_adjoint)
The rows below the panel [k0, end), L₂₁ = A₂₁ L₁₁⁻ᴴ: chunks of detail::cholesky_rows rows in parallel...
matrix< T > lower() const &
The lower-triangular factor L (with L Lᴴ == A).
void update_trailing(std::size_t k0, std::size_t end, const buffer< T > &minus_adjoint, buffer< T > &packing)
A22 += L21 (−L21ᴴ), by tiles of detail::cholesky_update_columns columns and detail::cholesky_update_r...
buffer< T > lower_
The lower-triangular factor L, row-major (upper triangle zero).
Thrown when an iterative algorithm has not converged within its bound of sweeps.
convergence_error(const std::string &message)
Builds the error.
Thrown when an operation's operands have incompatible shapes.
The eigenvalues and eigenvectors of a complex Hermitian matrix.
matrix< std::complex< double > > vectors_
Eigenvectors as columns.
hermitian_eigen(const matrix< std::complex< double > > &input, eigen_method method=eigen_method::divide_and_conquer)
Diagonalises input.
const matrix< std::complex< double > > & vectors() const &noexcept
The eigenvectors as columns, ordered to match values.
std::size_t n_
The matrix order.
matrix< std::complex< double > > vectors() &&
The eigenvectors, moved out of an expiring decomposition.
vector< double > values_
Eigenvalues, ascending.
const vector< double > & values() const noexcept
The eigenvalues, ascending.
An LU factorisation with partial pivoting of a square matrix.
T determinant() const
The determinant (the pivot sign times the product of U's diagonal; 0 if singular).
void swap_rows_outside(std::size_t a, std::size_t b, std::size_t k0, std::size_t end)
Swaps rows a and b of the factor outside the panel's columns [k0, end): right of the panel up to swap...
std::vector< std::pair< std::size_t, std::size_t > > deferred_swaps_
Swaps left for the columns past it.
matrix< T > factors() const &
L and U in one matrix, as LAPACK's dgetrf leaves them: U on and above the diagonal,...
void update_columns(const detail::packed_left< T > &left, std::size_t k0, std::size_t c0, std::size_t c1)
A₂₂ −= L₂₁ U₁₂ on the trailing columns [c0, c1), as A₂₂ += (−L₂₁) U₁₂ through the matrix product kern...
void solve_rows(buffer< T > &y, std::size_t cols, bool lower=false) const
Performs the pivoted elimination, filling lu_ and pivots_.
std::size_t column_slices(std::size_t cols) const
The column slices solve_rows splits cols right-hand sides into, one a thread, each of detail::slice_c...
const std::vector< std::size_t > & pivots() const noexcept
The row permutation: row i of P A = L U is row pivots()[i] of A.
void factorize_columns(buffer< T > &panel, std::size_t rows, std::size_t first, std::size_t last, std::size_t k0, std::size_t end)
Factorises the columns [first, last) of the panel (column-major, rows rows), every row from first dow...
lu_decomposition(const matrix< T > &a)
Factorises a (in place, into combined L\U with a row permutation).
std::size_t panel_of(std::size_t i) const noexcept
The first row of the panel that holds row i.
vector< T > solve(const vector< T > &b) const
Solves A x = b using the stored factors.
matrix< T > factors() &&
factors, moved out of an expiring decomposition: no copy.
matrix< T > solve(matrix< T > &&b) const
solve on an expiring b, in its own storage: its rows permuted in place (cycle by cycle of the pivots,...
buffer< T > lu_
Combined L (below diag, unit) and U (on/above), row-major.
void negated_block_into(buffer< T > &out, std::size_t r0, std::size_t r1, std::size_t c0, std::size_t c1) const
negated_block into out, resized to it.
std::size_t lower_slice_start(std::size_t s, std::size_t slices, std::size_t cols) const
The first column of slice s of slices (slices itself: cols) where the forward pass is lower: column c...
matrix< T > pivoted_inverse(buffer< T > storage) const
(P A)⁻¹ = U⁻¹ L⁻¹, the inverse of the rows in their pivoted order, in storage (its elements discarded...
void transpose_panel(buffer< T > &panel, std::size_t rows, std::size_t k0, std::size_t end, bool in)
Copies the columns [k0, end) of the factor, every row from k0, into panel (column-major,...
std::vector< std::size_t > pivots_
Row permutation: row now at i came from pivots_[i].
void solve_panel_rows(std::size_t k0, std::size_t end)
U₁₂ = L₁₁⁻¹ A₁₂: the panel's rows right of it, by forward substitution with its unit-lower L....
void swap_left_columns()
Applies left_swaps_ to the columns left of each swap's panel: a panel's columns take,...
void subtract_block(buffer< T > &y, std::size_t cols, std::size_t r0, std::size_t r1, std::size_t c0, std::size_t c1, std::size_t first, std::size_t last) const
Y[r0, r1) −= F[r0, r1) × [c0, c1) · Y[c0, c1), F the factor, through the matrix product kernel (nothi...
std::size_t block_
The panel width.
matrix< T > inverse(buffer< T > storage) const
inverse in storage, its elements discarded: no fresh n × n mapped and faulted in where it holds that ...
int sign_
Permutation sign, for the determinant.
void solve_columns(buffer< T > &y, std::size_t cols, std::size_t c0, std::size_t c1, bool lower) const
solve_rows on the columns [c0, c1) of y (n × cols, row-major): the blocked passes,...
bool singular() const noexcept
Whether a pivot was zero: U is singular, and solve and inverse throw.
void factorize_panel(buffer< T > &panel, std::size_t k0, std::size_t end)
Unblocked LU with partial pivoting of the columns [k0, end), every row below k0.
std::pair< T, double > log_determinant() const
The determinant as its unit-modulus sign and the natural logarithm of its magnitude,...
std::size_t n_
The matrix order.
bool singular_
Set if a zero pivot was met.
std::size_t swap_limit_
The columns a panel's row swaps reach now.
void factorize_trailing(buffer< T > &panel, std::size_t first_end)
The panels after the first (whose columns end at first_end), each with its trailing update....
matrix< T > solve(const matrix< T > &b) const
Solves A X = B for every column of b at once.
std::vector< std::pair< std::size_t, std::size_t > > left_swaps_
Swaps left for the columns before their panel.
matrix< T > inverse() const
The inverse, U⁻¹ L⁻¹ P, by blocks of rows through the matrix product kernel: L⁻¹ from the identity,...
lu_decomposition(matrix< T > &&a)
Factorises a in its own storage, an expiring matrix's elements taken over: no copy of them.
void solve_narrow(buffer< T > &y, std::size_t cols) const
solve_rows for a few right-hand sides: row by row, each one's terms in the order the blocked passes g...
buffer< T > negated_block(std::size_t r0, std::size_t r1, std::size_t c0, std::size_t c1) const
The rows [r0, r1) and columns [c0, c1) of the factor, negated, row-major and contiguous.
A dense, row-major matrix of T with value semantics.
buffer< T >::const_iterator end() const noexcept
Const iterator past the last element.
buffer< T >::const_iterator begin() const noexcept
Const iterator to the first element (row-major).
std::size_t rows() const noexcept
The number of rows.
std::size_t cols() const noexcept
The number of columns.
Thrown when a decomposition's input holds an infinite or NaN entry: no factorisation of it means anyt...
non_finite_error(const std::string &message)
Builds the error.
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.
std::size_t order_
The order of the failing leading minor, or 0.
not_positive_definite_error(const std::string &message, std::size_t order=0)
Builds the error.
A QR factorisation A = Q R of an m × n matrix, as NumPy's qr, reduced by default.
vector< T > solve(const vector< T > &b) const
The least-squares solution of A x ≈ b (x = R⁻¹ Qᴴ b) for a full-rank A.
std::size_t depth_
min(m, n).
qr_decomposition(matrix< T > &&a, qr_mode mode=qr_mode::reduced)
Factorises a in its own storage, an expiring matrix's elements taken over: no copy of them.
std::size_t cols_
The number of columns (n).
qr_decomposition(const matrix< T > &a, qr_mode mode=qr_mode::reduced)
Factorises a.
std::size_t width_
Q's columns and R's rows: k, or m complete.
std::size_t rows_
The number of rows (m).
matrix< T > q() &&
Q, moved out of an expiring decomposition (r stays).
matrix< T > q() const &
The factor Q (orthonormal columns): m × k, k = min(m, n), or m × m complete.
buffer< T > q_
The factor Q, row-major, m × width_.
buffer< T > r_
The upper-trapezoidal factor R, row-major, width_ × n.
matrix< T > r() &&
R, moved out of an expiring decomposition (q stays).
matrix< T > r() const &
The upper-trapezoidal factor R: k × n, k = min(m, n), or m × n complete.
The magnitude at or under which a singular value counts as zero, in a rank (matrix_rank),...
constexpr rank_cutoff() noexcept=default
NumPy's default: the largest singular value times the machine epsilon times the larger dimension.
static rank_cutoff absolute(double magnitude)
magnitude itself, whatever the largest singular value.
static double checked(double value)
constexpr rank_cutoff(double value, bool absolute) noexcept
rank_cutoff scaled(int exponent) const noexcept
The same cutoff for the matrix divided by 2^exponent.
double magnitude(double largest, std::size_t rows, std::size_t cols) const noexcept
The cutoff for a rows × cols matrix whose largest singular value is largest.
Thrown when a matrix has no unique solution (it is singular).
singular_matrix_error(const std::string &message)
Builds the error.
The singular value decomposition A = U Σ Vᴴ of an m × n matrix.
matrix< T > u_
m×k left singular vectors.
static void orthogonalise(buffer< T > &x, const buffer< T > &basis, std::size_t n, std::size_t count)
Removes from x its components along the first count columns of the n × n basis.
const matrix< T > & v() const &noexcept
The right singular vectors V (columns): n × k, or n × n full.
matrix< T > v_
n×k right singular vectors.
const matrix< T > & u() const &noexcept
The left singular vectors U (columns): m × k, or m × m full.
svd_decomposition(const matrix< T > &a, svd_method method=svd_method::divide_and_conquer, bool full=false)
Decomposes a.
matrix< T > vh() const
Vᴴ (k × n, or n × n full), so that A = U Σ Vᴴ reads u() * diag(singular_values()) * vh() (full,...
matrix< T > u() &&
U, moved out of an expiring decomposition (v and the singular values stay).
vector< double > singular_values_
Singular values, descending.
static matrix< T > left_vectors(buffer< T > q, buffer< T > left, std::size_t rows, std::size_t cols, std::size_t width)
U from the Householder q (rows × width) and the left vectors left of R (cols × cols): Q times them,...
void factor(const matrix< T > &a, svd_method method, bool full)
Decomposes an m × n a with m ≥ n into u_, singular_values_ and v_, U square when full.
const vector< double > & singular_values() const noexcept
The singular values, descending.
The eigenvalues and eigenvectors of a real symmetric matrix.
matrix< double > vectors_
Eigenvectors as columns.
const matrix< double > & vectors() const &noexcept
The eigenvectors as columns, ordered to match values.
matrix< double > vectors() &&
The eigenvectors, moved out of an expiring decomposition.
void extract_sorted(const buffer< double > &diagonal, const buffer< double > &v)
Sorts the eigenvalues diagonal ascending and reorders v's columns to match.
symmetric_eigen(const matrix< double > &input, eigen_method method=eigen_method::divide_and_conquer)
Diagonalises input.
vector< double > values_
Eigenvalues, ascending.
std::size_t n_
The matrix order.
const vector< double > & values() const noexcept
The eigenvalues, ascending.
A dense vector of T with value semantics.
buffer< T >::const_iterator end() const noexcept
Const iterator past the last element.
std::size_t size() const noexcept
The number of elements.
buffer< T >::const_iterator begin() const noexcept
Const iterator to the first element.
#define NUMERICAL_UNROLL_BLOCK
#define NUMERICAL_OUT_OF_LINE
A dense, row-major, value-semantic matrix over a scalar type.
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...
T symmetric_row(const T *row, const T *v, const T &f, T *out, std::size_t count)
Σ row[i] · v[i] and out[i] += row[i] · f, in order, for a type no native vector holds.
constexpr std::size_t lu_lookahead_columns
The trailing columns at most past which the LU factorises its next panel beside the update of the res...
constexpr std::size_t qr_transpose_tile
The side of the tiles in which the QR transposes (transpose_into): a tile's rows and columns both sta...
double rescale_tiny(buffer< T > &x)
Scales x by rescue_factor of its largest magnitude, exactly, so that a Householder vector built from ...
arrow_workspace & thread_arrow_workspace()
The calling thread's arrow_workspace, kept from one decomposition to the next. A join uses it whole o...
double jacobi_off_diagonal(const buffer< double > &a, std::size_t n)
The Frobenius norm of the strict off-diagonal of the n × n a.
constexpr std::size_t lu_block_for(std::size_t n) noexcept
The panel width of the LU of order n: a power of two, so a row's panel is a mask away,...
void factor_qr_panel(T *panel, std::size_t ld, std::size_t top, std::vector< buffer< T > > &reflectors, std::vector< T > &taus, std::size_t k0, std::size_t k1)
Householder reflections of the columns k0…k1-1 of a column-major matrix, rows from k0,...
constexpr std::size_t cholesky_update_rows
The rows of one tile of a Cholesky update: the tiles of a panel's update, each one product on one thr...
matrix< T > inverse_on_kept(const vector< double > &sigma, const matrix< T > &right, const matrix< T > &left, rank_cutoff cutoff)
Σ_d right_d (1 / σ_d) left_dᴴ over the columns d of right (cols × k) and left (rows × k) whose sigma ...
bool all_finite_shared(const double *x, std::size_t count)
all_finite on the threads by chunks of finite_chunk, past two of them: a pass bound by the memory,...
double rotation_length(double a, double b) noexcept
√(a² + b²) for a plane rotation of the QR and QL iterations, as LAPACK's dlartg takes it: the plain s...
constexpr std::size_t cholesky_block
The columns of one panel of the blocked Cholesky factorisation (as lu_block).
void parallel_for_beside(std::size_t count, std::size_t work_per_index, const Body &body, const Side &side)
Runs side on the caller while the pool's workers run body(i) for every i in [0, count),...
void bidiagonal_qr(buffer< double > &d, buffer< double > &e, buffer< T > &ut, buffer< T > &vt, std::size_t n, std::size_t max_steps)
The singular values of the real upper bidiagonal (d, e) by implicit-shift QR with deflation (LINPACK'...
vector< double > hermitian_singular_values(const matrix< T > &a)
The singular values of the real symmetric or complex Hermitian matrix whose lower triangle is a's: of...
matrix< T > hermitian_pseudo_inverse(const matrix< T > &a, rank_cutoff cutoff)
The pseudo-inverse of the real symmetric or complex Hermitian matrix whose lower triangle is a's: fro...
double rescue_factor(double largest)
The exact power of two that brings a magnitude below about the square root of the smallest normal num...
constexpr double rescue_below
The magnitude under which rescue_factor scales a T up: about the square root of the smallest normal n...
bool lower_is_finite(const matrix< T > &a)
Whether the lower triangle of the square a, and its diagonal's real part, are finite: entry by entry,...
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...
constexpr std::size_t lu_update_speedup
The multiply-adds of an LU's trailing update one of the tile's dependent ones stands for,...
void subtract_corrections(T *y, const T *f, const T *x, std::size_t ldx, std::size_t terms, std::size_t count)
subtract_products of a real T, where the reduction's corrections are those of its type.
void add_conjugate_product(T *y, const T *x, const T &f, std::size_t count)
y[i] += conj(x[i]) · f for a real T, where the conjugate is the value: add_product.
void subtract_rows(double *y, std::size_t ldy, const double *f, std::size_t ldf, const double *x, std::size_t ldx, std::size_t terms, std::size_t count)
subtract_rows for double, the units chosen as subtract_products chooses them: each element the value ...
@ plain
gemm_rows, or a scalar whose products are not shared: A is read in place.
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.
constexpr std::size_t cholesky_together_width
The narrowest Cholesky diagonal block whose rows go together: in a block of 8, the groups' loops cost...
constexpr double plain_squares_low
The smallest plain sum of squares plain_or_at_scale keeps: a term that underflowed is below 2⁻¹⁰²²,...
constexpr std::size_t gemm_kernel_speedup
The multiply-adds the register tile does in the time of one in work_per_thread's dependent chain (abo...
constexpr std::size_t symmetric_row_group
The rows of a lower triangle symmetric_rows takes at once.
buffer< T > upper_inverse(const buffer< T > &r, std::size_t n)
R⁻¹, upper triangular, of the n × n upper-triangular r (row-major): back substitution against the ide...
qr_reflections< T > householder_reflect(buffer< T > a, std::size_t rows, std::size_t cols)
The reflections of householder_qr, without forming Q: A = Q R with Q the product of the blocks' refle...
constexpr std::size_t divide_leaf
The order up to which tridiagonal_divide_and_conquer diagonalises by implicit QL.
T operator_times(const T &a, const T &b)
a · b, as std::operator* computes it, in line. libc++'s complex product (the four products and their ...
void solve_bidiagonal_leaf(bidiagonal_node &node)
Decomposes the leaf node by implicit-shift QR.
constexpr std::size_t vector_speedup
The multiply-adds a vectorised inner loop does in the time of one in work_per_thread's dependent chai...
void merge_rank_one(buffer< double > &d, buffer< double > z, double rho, double *q, std::size_t ldq, std::size_t n, std::size_t split, merge_workspace &work)
The eigenvalues of diag(d) + ρ z zᵀ, ascending, into d, and its eigenvectors times q (the row-major n...
void one_sided_jacobi(buffer< T > &w, buffer< T > &v, std::size_t n, std::size_t max_sweeps)
One-sided (Hestenes) Jacobi on the columns of the n × n row-major w: plane rotations,...
vector< double > eigenvalue_magnitudes(const matrix< T > &a)
The magnitudes of the eigenvalues of the real symmetric or complex Hermitian a, its lower triangle re...
constexpr std::size_t slice_columns
The fewest columns an LU solve gives a thread of its own: a narrower slice starves the product kernel...
constexpr std::size_t cholesky_update_columns
The fewest columns of the trailing block one Cholesky update's product takes: each product packs ever...
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.
constexpr std::size_t jacobi_sweeps
The sweeps a Jacobi method may take: it converges quadratically, in under 15 on every matrix measured...
std::array< double, symmetric_row_group >(*)(const double *, std::size_t, const double *, double *, std::size_t) symmetric_rows_kernel
A kernel of symmetric_rows' form: symmetric_rows itself, or an x86-64 one.
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,...
void require_finite(const matrix< T > &a, const char *what)
The one non-finite policy, checked where every decomposition starts.
T hermitian_row(symmetric_row_kernel, const T *row, const T *v, T *out, std::size_t i)
Row i's terms of a Hermitian product from the lower triangle: out[j] += conj(row[j]) v[i] for j < i,...
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...
constexpr std::size_t tridiagonal_block
The reflections of one panel of the blocked tridiagonal reduction.
void tridiagonal_ql(buffer< double > &d, buffer< double > &e, buffer< T > &zt, std::size_t n, std::size_t max_steps)
The eigenvalues of the symmetric tridiagonal (d, e) by implicit QL with Wilkinson-type shifts (EISPAC...
constexpr std::size_t work_per_thread
The work one thread must have to pay for starting it: about 60 µs of multiply-adds,...
double secular_root(const buffer< double > &d, const buffer< double > &z, double rho, std::size_t i, std::size_t max_iterations, double *delta)
Root i of 1/ρ + Σⱼ zⱼ²/(dⱼ − λ) = 0 (d strictly ascending, ‖z‖ = 1), the i-th eigenvalue of diag(d) +...
void parallel_copy(const T *in, std::size_t count, T *out)
Copies the count elements at in to out, split across the threads the work pays for,...
void join_bidiagonal_node(bidiagonal_node &node, const bidiagonal_node &one, const bidiagonal_node &two, arrow_workspace &work)
Joins the two decomposed halves of node, one and two, by merge_arrow, node started (start_bidiagonal_...
buffer< T > tridiagonal_basis(const tridiagonal_reduction< T > &reduction, std::size_t n)
Mᵀ of a reduction, row-major (its rows are the columns of M): Qᵀ by blocks of reflectors from the las...
void reflect_columns(T *panel, std::size_t height, const buffer< T > &v, std::size_t first, std::size_t from, std::size_t to, const T &s=T {2})
Applies I − s v vᴴ from the left (s 2 for a unit v) to the columns from…to-1 of the column-major pane...
constexpr std::size_t ql_steps_per_value
The implicit QL steps the tridiagonal eigensolver may take per eigenvalue, as LAPACK's dsteqr bounds ...
void gemm_with(const packed_left< T > &left, std::size_t n, const T *b, std::size_t ldb, T *c, std::size_t ldc)
C += A·B for the packed left (pack_left) and the k × n b, as gemm computes it, A read in place here,...
T conj_of(const T &x)
The conjugate of a real (or general) scalar — the value itself.
std::size_t plan_bidiagonal(std::vector< bidiagonal_node > &nodes, const double *d, const double *e, std::size_t n, std::size_t extra, std::size_t depth)
Appends the block (d, e, n, extra) and everything below it to nodes, parents before children,...
std::vector< rotation_run > rotation_runs(const std::vector< row_operation > &operations)
The operations split into runs of chained rotations, every other operation a run of one.
constexpr std::size_t kept_workspace
The most elements a buffer of a thread's merge_workspace keeps from one decomposition to the next: 2 ...
constexpr std::size_t cholesky_block_for(std::size_t n) noexcept
The panel width of the Cholesky factorisation of order n: narrower up to order 256,...
void release_large_arrow_workspace(arrow_workspace &work)
Releases the buffers of work longer than kept_workspace.
void factor_row_major_panel(buffer< T > &a, std::size_t rows, std::size_t cols, std::vector< buffer< T > > &reflectors, std::vector< T > &taus, std::size_t k0, std::size_t k1)
factor_qr_panel on the columns k0…k1-1 of the row-major a, rows from k0, in a column-major copy of th...
constexpr std::size_t tridiagonal_band
The rows of each band of the tridiagonal reduction's trailing update: a band reaches its diagonal,...
double norm_at_scale(const T *x, std::size_t count)
The Euclidean norm of the count elements at x, summed at the scale of their largest magnitude: a plai...
constexpr double qr_rank_margin
How far above the rank cutoff a bound on the smallest singular value must stay for lstsq_by_qr to ans...
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...
constexpr std::size_t lu_small_order
The order up to which the LU takes panels of lu_small_block columns: there the panels,...
void transpose_into(const T *from, std::size_t rows, std::size_t cols, T *to)
The row-major rows × cols from into to as its transpose, cols × rows row-major, by tiles,...
tridiagonal_reduction< T > tridiagonalize(buffer< T > &a, std::size_t n, buffer< double > &d, buffer< double > &e)
Reduces the Hermitian (real: symmetric) n × n row-major a, of which the lower triangle is read,...
void subtract_product(double *y, const double &f, const double *x, std::size_t count)
y[i] −= f · x[i] for double on the widest x86-64 units the matrix product's kernel may use (gemm_path...
bool all_finite(const double *x, std::size_t count) noexcept
Whether the count values at x are all finite: a NaN or an infinity has every exponent bit set,...
void require_finite_lower(const matrix< T > &a, const char *what)
require_finite over what a Hermitian eigensolver reads: the lower triangle, and the diagonal's real p...
constexpr std::size_t bidiagonal_block
The rows from which a step of the blocked bidiagonal reduction shares its coefficients and correction...
void solve_divide_subtree(std::vector< divide_node > &nodes, std::size_t index, merge_workspace &work)
Solves the block index of nodes and everything below it, each block right after its halves.
packed_left< T > pack_left(std::size_t m, std::size_t, std::size_t k, const T *a, std::size_t lda, buffer< T > &)
The m × k a packed once for products with it of about n columns each (gemm_with), in storage (grown a...
constexpr std::size_t bidiagonal_crossover
The order below which a real bidiagonal reduction goes a reflection at a time, as LAPACK's dgebrd fal...
void bidiagonal_divide_and_conquer(buffer< double > &d, buffer< double > &e, buffer< T > &ut, buffer< T > &vt, std::size_t n)
The singular values of the upper bidiagonal (d, e) into d, descending, the singular vectors accumulat...
void form_bidiagonal_bases(const std::vector< buffer< T > > &left, std::vector< buffer< T > > right, const buffer< T > &left_phase, const buffer< T > &right_phase, std::size_t n, buffer< T > &ut, buffer< T > &vt)
The bases of a bidiagonal reduction from its reflectors: U_b = (H₀ H₁ …) diag(conj left phases) and V...
constexpr std::size_t finite_chunk
The elements all_finite_shared gives a thread at a time.
std::array< T, symmetric_row_group > symmetric_rows(const T *rows, std::size_t n, const T *v, T *out, std::size_t i)
symmetric_rows one column at a time, for a type no native vector holds.
double rank_tolerance(double largest, std::size_t rows, std::size_t cols)
The magnitude under which a diagonal entry of R or a singular value counts as zero,...
double quadratic_step(double a, double b, double c)
The smaller root η of C η² − A η + B = 0, in the form that does not cancel: a step of solve_secular's...
double * workspace_of(workspace_buffer &buffer, std::size_t size)
buffer at least size elements long, its storage kept for the next join. A larger one is mapped fresh,...
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...
std::optional< matrix< T > > lstsq_by_qr(const matrix< T > &a, const matrix< T > &b, rank_cutoff cutoff, std::pair< buffer< T >, int > *const r=nullptr)
The least-squares solution of A X ≈ B by a QR factorisation, of A, or of Aᴴ for a wide A (the minimum...
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.
void subtract_term(T &sum, const T &a, const T &b)
sum −= a · b in one statement, which the compiler may fuse (as it fuses the matrix solve's).
void apply_reflector_block(const reflector_block< T > &block, bool adjoint, buffer< T > &a, std::size_t cols, std::size_t from, std::size_t to)
Applies I − V T Vᴴ (or its adjoint I − V Tᴴ Vᴴ) from the left to the block's rows of a row-major arra...
double plain_or_at_scale(double plain, const T *x, std::size_t count)
√plain, the plain sum of the squares of the count elements at x, where it neither overflowed nor unde...
buffer< T > columns_by_value(const buffer< T > &zt, const std::vector< std::size_t > &order, std::size_t n)
The n × n row-major matrix whose column c is row order[c] of zt (the eigenvectors as rows): by tiles,...
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).
void cyclic_jacobi(buffer< double > &a, buffer< double > &v, std::size_t n, double frobenius, std::size_t max_sweeps)
Cyclic Jacobi on the full symmetric a, the rotations accumulated into v.
void merge_arrow(buffer< double > &d, buffer< double > z, double alpha, double beta, buffer< double > &u, buffer< double > &v, std::size_t n, std::size_t m, std::size_t split, arrow_workspace &work)
The singular value decomposition of the arrow M (first row z, diagonal d, d₀ = 0),...
std::conditional_t< std::is_same_v< T, float >||std::is_same_v< T, std::complex< float > >, float, double > value_range_t
The real type whose range a T's values take: float for a single-precision scalar, real or complex; do...
void join_bidiagonal_halves(std::vector< bidiagonal_node > &nodes, std::size_t index, arrow_workspace &work)
Joins the halves of nodes' block index in work, then lets them go.
matrix< T > hermitian_from_lower(const matrix< T > &a)
The real symmetric or complex Hermitian matrix whose lower triangle is the square a's,...
void hermitian_product(const T *block, std::size_t n, std::size_t m, const T *v, T *w, buffer< T > &scratch)
w = B v for the Hermitian m × m block (row stride n) from its lower triangle alone,...
constexpr double jacobi_large
The |ζ| past which one-sided Jacobi takes the rotation's tangent as 1 / (2|ζ|): ζ² stays finite under...
std::size_t numerical_rank(const vector< double > &sigma, std::size_t rows, std::size_t cols, rank_cutoff cutoff={})
How many of the singular values sigma of an rows × cols matrix exceed cutoff: its numerical rank (the...
double real_part(const T &x)
The real part of a real (or general) scalar as a double.
decltype(std::abs(std::declval< const T & >())) magnitude_t
The type std::abs gives for a T: the real type of a complex one, T itself otherwise.
T over_pivot(const T &x, const T &divisor)
x over the pivot whose pivot_divisor is divisor: the same quotient wherever the LU takes it.
gemm_path gemm_path_current() noexcept
The path the next double product takes: the most capable one under the cap that runs here (each path'...
std::vector< std::size_t > ascending_order(const buffer< double > &diagonal)
The indices of diagonal by ascending value.
void solve_divide_leaf(const divide_node &node)
Solves the leaf node by implicit QL: its eigenvalues ascending into its diagonal, their eigenvectors ...
void release_large_workspace(merge_workspace &work)
Releases the buffers of work longer than kept_workspace.
constexpr std::size_t lu_small_block
The columns of one panel of the LU of an order up to lu_small_order.
void solve_bidiagonal_subtree(std::vector< bidiagonal_node > &nodes, std::size_t index, arrow_workspace &work)
Decomposes the block index of nodes and everything below it, each block right after its halves.
double real_subdiagonal(double t, double, double &)
A real subdiagonal entry of the tridiagonal: kept with its sign (the rotations take any sign).
void subtract_panel_columns(T *column, const T *columns, std::size_t height, const T *l, std::size_t terms)
column −= Σₜ x_t · conj(l[t]) over the terms columns columns (height elements each,...
constexpr std::size_t cholesky_one_panel
The order below which the Cholesky factorisation is one panel: no trailing product,...
bool is_finite_entry(const T &x)
Whether x is finite.
constexpr std::size_t first_touch_chunk
The elements a task of workspace_of touches first: 64 pages of 4 KiB, so a buffer of a few megabytes ...
void apply_row_operations(buffer< T > &m, std::size_t n, const std::vector< row_operation > &operations)
Applies the recorded operations, in order, to the row-major m (n per row). Each column takes them ind...
constexpr std::size_t solve_leaf
The columns of one leaf of the recursive LU panel.
matrix< T > parallel_copy_of(const matrix< T > &a)
A copy of a whose pages the threads fault in (parallel_copy), a decomposition's own storage: the matr...
buffer< double > square_singular_values(buffer< T > b, std::size_t n)
The singular values of the square n × n row-major b, descending, without its vectors: its Golub–Kahan...
constexpr std::size_t qr_block
The reflectors of one panel of the blocked QR.
constexpr std::size_t lu_block
The columns of one panel of the blocked LU: the trailing update, a product of this depth,...
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,...
void reflect_rows(buffer< T > &a, std::size_t cols, const buffer< T > &v, std::size_t first, std::size_t from)
Applies I − 2 v vᴴ from the left to the rows first…first+|v|-1 of a row-major array,...
void apply_transposed_reflector_block(const reflector_block< T > &block, buffer< T > &at, std::size_t cols, std::size_t from, std::size_t to, bool adjoint=false)
apply_reflector_block to A held transposed: at is Aᵀ (row-major, cols columns), and A ← (I − V T Vᴴ) ...
void rotate_columns(buffer< double > &m, std::size_t cols, std::size_t a, std::size_t b, double c, double s)
Columns a and b of the row-major m (cols per row) rotated: a ← c a + s b, b ← c b − s a.
void bidiagonal_divide(const double *d, const double *e, std::size_t n, std::size_t extra, buffer< double > &sigma, buffer< double > &u, buffer< double > &v)
The singular value decomposition of the upper bidiagonal n × (n + extra) (extra 0 or 1) with diagonal...
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 divide_and_conquer(double *d, const double *e, std::size_t n, buffer< double > &q)
The eigenvalues, ascending, of the symmetric tridiagonal with diagonal d[0…n-1] and subdiagonal e[0…n...
constexpr std::size_t lowner_block
The poles a task of merge_rank_one's Löwner products takes, and the side of its tiles of u.
constexpr std::size_t secular_iterations
The iterations one root of the secular equation may take: it converges in a few; the bound turns a no...
T scaled_by_two(const T &x, int exponent)
x times two to the exponent, exactly where the result is a normal number (no power of two is formed,...
constexpr std::size_t corrected_product_least
The rows from which corrected_hermitian_product shares its coefficients and bands with the threads: o...
void join_divide_node(const divide_node &node, merge_workspace &work)
Joins the two solved halves of node by merge_rank_one, in work.
matrix< T > lstsq_from_svd(const svd_decomposition< T > &s, std::size_t rank, const matrix< T > &b)
The minimum-norm least-squares solution of A X ≈ B from the singular value decomposition s of A (m × ...
constexpr std::size_t bidiagonal_leaf
The order up to which bidiagonal_divide decomposes by implicit-shift QR.
constexpr std::size_t product_rows
The rows of y subtract_rows takes at once.
constexpr std::size_t svd_band
The rows of an SVD's input one thread scales and copies at a time.
constexpr std::size_t inverse_band
The rows an LU inverse writes its identity into, and permutes the columns of, on one thread at a time...
void plan_divide(std::vector< divide_node > &nodes, double *d, const double *e, std::size_t n, double *q, std::size_t ldq, std::size_t depth)
Appends the block (d, e, n) and, before it is solved, everything below it to nodes,...
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,...
double singular_secular_root(const buffer< double > &d, const buffer< double > &z, std::size_t i, std::size_t max_iterations, double *below, double *above)
Singular value i of the arrow M whose first row is z and whose diagonal is d (d₀ = 0,...
matrix< T > adjoint(const matrix< T > &a)
The conjugate transpose of a.
void to_matrix_basis(const tridiagonal_reduction< T > &reduction, buffer< T > &at, std::size_t n)
The rows of at, vectors in the tridiagonal's basis, taken to the matrix's: at ← at Mᵀ = at Dᴴ Qᵀ,...
void unscale_divide_node(const divide_node &node)
The eigenvalues of a solved node, scaled back, where its parent reads them.
void gemm_rows(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 by the plain i-k-j loop (gemm_rows_rounded), each element rounded as the tile of the path in...
constexpr double plain_squares_high
The largest plain sum of squares plain_or_at_scale keeps: no term of it overflowed.
constexpr std::size_t bidiagonal_product_weight
The weight of a multiply-add of the bidiagonal reduction's matrix-vector products,...
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,...
secular_solution solve_secular(const buffer< double > &z, double rho, std::size_t i, std::size_t max_iterations, double *delta, const Offset &offset)
Root i of 1/ρ + Σⱼ zⱼ²/(Pⱼ − x) = 0, poles P strictly ascending, no zⱼ zero, ρ > 0,...
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.
merge_workspace & thread_workspace()
The calling thread's merge_workspace, kept from one decomposition to the next: mapping its pages anew...
void jacobi_rotate(buffer< double > &a, buffer< double > &v, std::size_t n, std::size_t p, std::size_t q)
One Jacobi rotation of the n × n a zeroing a[p][q], accumulated into v.
constexpr std::size_t svd_qr_steps_per_value
The QR steps the bidiagonal singular value iteration may take per value: it converges in two or three...
void unscale_bidiagonal_node(bidiagonal_node &node)
The singular values of a solved node scaled back.
constexpr std::size_t bidiagonal_basis_crossover
The order below which the bases of a bidiagonal reduction are formed a reflector at a time: the block...
void bidiagonalize(buffer< T > &b, std::size_t n, buffer< double > &d, buffer< double > &e, buffer< T > &ut, buffer< T > &vt, bool form=true)
Reduces the real square n × n row-major b to upper-bidiagonal form, B = U_b D V_bᵀ,...
buffer< T > tridiagonal_divide_and_conquer(buffer< double > &d, buffer< double > &e, const tridiagonal_reduction< T > &reduction, std::size_t n)
The eigenvalues of the tridiagonal (d, e) of reduction into d, and the matrix's eigenvectors as the r...
void start_bidiagonal_node(bidiagonal_node &node)
Its singular values and vectors into node's, from scratch: u zero, v the identity.
thin_qr< T > householder_qr(buffer< T > a, std::size_t rows, std::size_t cols, bool complete=false)
Factorises the rows × cols row-major a by Householder reflections, A = Q R with k = min(rows,...
void multiply_real_left(const buffer< double > &left, buffer< T > &zt, std::size_t n)
zt ← left · zt, n × n, row-major, for any other element type: the real factor converted.
T pivot_divisor(const T &pivot)
The divisor over_pivot takes for the pivot pivot. A complex pivot's reciprocal, once,...
constexpr std::size_t zeroed_rows
The rows of a Cholesky factor whose upper part one thread zeroes at a time.
constexpr std::size_t cholesky_rows
The rows of one chunk of a Cholesky panel's rows below it, solved together along each column.
void bidiagonalize_unblocked(buffer< T > &b, std::size_t n, buffer< double > &d, buffer< double > &e, buffer< T > &ut, buffer< T > &vt, bool form=true)
Reduces the square n × n row-major b to real upper-bidiagonal form, B = U_b D V_bᴴ with D real,...
double(*)(const double *, const double *, double, double *, std::size_t) symmetric_row_kernel
A kernel of symmetric_row's form: symmetric_row itself, or an x86-64 one.
constexpr std::size_t cholesky_diagonal_rows
The rows of a Cholesky diagonal block factorised together, one chain of sums each (one x86-64 AVX2 co...
T conjugate_dot(const T *x, const T *y, std::size_t count)
Σ conj(x[i]) · y[i] for a real T, where the conjugate is the value: dot_product.
T from_double(double x)
x, a magnitude carried in double, as the scalar T: converted to T's real type first,...
constexpr std::size_t memory_element_weight
The weight of an element a pass bound by the memory writes or moves, against a multiply-add,...
void corrected_hermitian_product(const T *block, std::size_t n, std::size_t m, const T *v, T *w, buffer< T > &scratch, const T *corrections, std::size_t ldc, std::size_t terms, T *factors, T *next=nullptr, const T *next_factors=nullptr)
hermitian_product corrected by a panel's earlier steps: w = B v − Σₜ fₜ cₜ, the coefficients f[2p] = ...
constexpr double jacobi_tiny
The sum of squares under which one-sided Jacobi judges a pair of columns over their norms: below it a...
std::size_t symmetric_row_speedup()
The weight of a symmetric row's multiply-adds in hermitian_product: on the x86-64 units,...
std::size_t largest_magnitude_vectors(const T *, std::size_t, Magnitude &, std::size_t &)
Only x[0], the one best and at come in with, for a type no native vector holds.
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.
T householder_in_place(buffer< T > &x)
The unit Householder vector v with (I − 2 v vᵀ) x = β e₁, in place of x, for a real x.
The NumericAL public API.
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...
std::size_t max_threads()
The threads a kernel may use: the cap if one is set, else the hardware threads (starting none).
cholesky_decomposition< T > cholesky(matrix< T > a)
Cholesky-factorises a Hermitian positive-definite matrix.
T sum(const tensor< T > &t)
The sum of every element, by pairwise halving (0 for an empty tensor).
vector< T > solve(matrix< T > a, const vector< T > &b)
Solves the linear system A x = b for a square A.
matrix< T > pinv(const matrix< T > &a, rank_cutoff cutoff={}, bool hermitian=false)
The Moore–Penrose pseudo-inverse of the m × n a (n × m), as numpy.linalg.pinv gives it: V Σ⁺ Uᴴ,...
T determinant(matrix< T > a)
The determinant of a square matrix.
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...
gemm_path
The kernels a double matrix product can run on (a float one too on arm64), from the most portable....
@ portable
The compiler's vectors (16 bytes on x86-64 wheels), as the compiler rounds c + a·b (see gemm).
@ amx
The Apple AMX coprocessor, fused multiply-adds; the products it does not suit on the NEON path's tile...
qr_mode
The factors a numerical::qr_decomposition keeps, as numpy.linalg.qr's modes name them.
@ reduced
Q is m × k, R k × n, k = min(m, n): NumPy's default.
@ complete
Q is m × m, its columns past k a basis of the complement of A's column space; R is m × n.
svd_method
How numerical::svd_decomposition computes the decomposition of R, after a Householder QR.
eigen_method
How numerical::symmetric_eigen diagonalises.
@ jacobi
Cyclic Jacobi rotations: several times slower, kept as an independent second method.
std::size_t matrix_rank(const matrix< T > &a, rank_cutoff cutoff={}, bool hermitian=false)
The numerical rank of a, as numpy.linalg.matrix_rank counts it: its singular values above cutoff.
qr_decomposition< T > qr(const matrix< T > &a)
QR-factorises an m × n matrix.
vector< T > lstsq(const matrix< T > &a, const vector< T > &b, rank_cutoff cutoff={})
The minimum-norm least-squares solution of A x ≈ b, as NumPy's lstsq gives it.
T min(const tensor< T > &t)
The smallest element (a NaN, if there is one).
vector< double > singular_values(const matrix< T > &a)
The singular values of a, descending, without its singular vectors, as numpy.linalg....
matrix< T > inverse(matrix< T > a)
The inverse of a square matrix.
vector< double > symmetric_eigenvalues(const matrix< T > &a)
The eigenvalues of the real symmetric or complex Hermitian a, ascending, without its eigenvectors,...
tensor< T > full(std::vector< std::size_t > shape, const T &value)
A tensor of shape with every element value.
A minimal std::thread parallel-for for the hot kernels.
The native vector of float and double the kernels hold their lanes in (GNU vector extensions,...
The buffers merge_arrow works in, kept by each thread from one join to the next (thread_arrow_workspa...
workspace_buffer above
poles[j] + roots[i].
workspace_buffer left
The arrow's left vectors.
workspace_buffer factor
The rows of the arrow's vectors for those columns.
workspace_buffer new_v
The kept columns of v times the right vectors.
workspace_buffer below
poles[j] − roots[i], row by root.
workspace_buffer new_u
The kept columns of u times the left vectors.
workspace_buffer right
Its right vectors.
buffer< double > sorted_u
u's columns by ascending value, swapped with u.
workspace_buffer columns
One segment's rows of the kept columns that reach it.
buffer< double > sorted_v
v's, swapped with v.
One block of bidiagonal_divide's tree: the bidiagonal it decomposes (its own copies scaled by a power...
int exponent
The power of two it is scaled down by.
std::size_t k
The row it splits at; 0 for a leaf.
std::size_t extra
Its columns past its rows, 0 or 1.
buffer< double > v
Its right singular vectors, as columns.
buffer< double > sigma
Its singular values, ascending.
const double * d
The diagonal it is solved on.
const double * e
The superdiagonal it is solved on.
buffer< double > u
Its left singular vectors, as columns.
One block of divide_and_conquer's tree: its rows [0, n) of the diagonal d its parent reads,...
double * d
The diagonal its parent reads, the eigenvalues once solved.
const double * work_e
The subdiagonal it is solved on.
std::size_t ldq
The distance between the rows of q.
double * q
Its diagonal block of the eigenvectors.
double * work_d
The diagonal it is solved on.
std::size_t n1
The order of its first half; 0 for a leaf.
int exponent
The power of two it is scaled down by.
double beta
The coupling of its halves.
The buffers of merge_rank_one, kept from one join to the next: a fresh buffer per join is mapped anew...
workspace_buffer updated
The kept columns of q times u.
workspace_buffer factor
The rows of u for those columns.
workspace_buffer gaps
poles[j] − roots[i], row by root, then the eigenvectors in its place.
workspace_buffer columns
One half's rows of the kept columns that reach it.
A left operand packed once for several products by it (an LU's −L₂₁ against blocks of its U₁₂): the t...
std::size_t k
A's columns, the depth.
The reflections of a Householder QR, Q left as its blocks of reflectors.
buffer< T > r
R, k × cols upper trapezoidal, its diagonal as it comes.
std::vector< reflector_block< T > > blocks
Q = H₀ H₁ …, block by block of qr_block reflectors.
The reflectors of one panel of the blocked QR, I − V T Vᴴ as LAPACK's dlarft forms it.
std::size_t height
The rows it acts on, first…first+height-1.
buffer< T > v
V, height × width row-major, reflector j from row j (zeros above).
buffer< T > t
T, width × width upper triangular.
std::size_t width
Its reflector count.
std::size_t first
The first row the block acts on.
A run of recorded rotations in which each shares a row with the next, as a sweep of the QL or QR iter...
std::size_t first
Its first operation.
bool down
Whether each next rotation's second row is this one's first.
std::size_t count
Its operations (one for an operation outside any run).
One operation of tridiagonal_ql or bidiagonal_qr on the rows of a vector matrix, recorded to be appli...
std::size_t j
The second row (unused by negate).
std::size_t i
The first row.
double c
The cosine of a rotation.
double s
The sine of a rotation.
A root of a secular equation: the pole it was sought from, and its offset from that pole.
std::size_t origin
The pole.
double offset
The root minus the pole.
A thin QR factorisation, row-major: q is rows × cols with orthonormal columns, r is cols × cols upper...
buffer< T > q
The orthonormal factor.
buffer< T > r
The triangular factor.
The unitary M = Q Dᴴ of a reduction to real tridiagonal form, A = M T Mᴴ, kept as its factors.
std::vector< buffer< T > > reflectors
Q = H₁ H₂ …: reflectors[k + 1] zeroes column k, from row k + 1.
buffer< T > delta
D = diag(δ), the unit phases that make T real.
One buffer of merge_workspace: its storage and how many elements it holds.
std::size_t size
Their number.
std::unique_ptr< double[]> data
The elements.
A least-squares solution and what numpy.linalg.lstsq reports beside it (least_squares).
vector< double > singular_values
The singular values of A, descending (singular_values).
matrix< T > solution
The minimum-norm x of A x ≈ B (n × k), lstsq's.
vector< double > residuals
Per column of B, ‖B − A x‖²: for a tall A of full column rank, else empty.
A dense, value-semantic vector over a scalar type.