Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
lu_common.hpp
Go to the documentation of this file.
1#pragma once
2
4#include <algorithm>
5#include <span>
7
8namespace uni20::linalg::detail
9{
10// Capture both norms in a column traversal before LU overwrites the original.
11template <class Matrix, Real R> void capture_lu_norms(Matrix const& a, LuNorms<R>* norms, R const& zero)
12{
13 if (!norms) return;
14 *norms = {zero, zero};
15 std::vector<R> row_sums(std::size_t(a.extent(0)), zero);
16 for (std::size_t j = 0; j < std::size_t(a.extent(1)); ++j)
17 {
18 R sum = zero;
19 for (std::size_t i = 0; i < std::size_t(a.extent(0)); ++i)
20 {
21 R magnitude = math::abs(typename Matrix::value_type(a[i, j]));
22 sum += magnitude;
23 row_sums[i] += magnitude;
24 }
25 norms->one = std::max(norms->one, sum);
26 }
27 for (auto const& sum : row_sums)
28 norms->infinity = std::max(norms->infinity, sum);
29}
30
31// Mathematical magnitude = scale * unit, with unit in [1,sqrt(2)]. Never
32// multiply these parts: the magnitude of finite complex components can overflow.
33template <Real R> struct LuMagnitude
34{
35 R scale;
36 R unit;
37};
38
39template <RealOrComplex S> auto lu_magnitude(S const& value)
40{
41 using R = make_real_t<S>;
42 R scale = math::abs(uni20::real(value));
43 R unit = scalar_like(scale, 1);
44 if constexpr (Complex<S>)
45 {
46 R other = math::abs(uni20::imag(value));
47 if (other > scale) std::swap(other, scale);
48 if (scale != 0)
49 {
50 auto ratio = other / scale;
51 unit = math::sqrt(unit + ratio * ratio);
52 }
53 }
54 return LuMagnitude<R>{std::move(scale), std::move(unit)};
55}
56
57template <Real R> bool lu_magnitude_less(LuMagnitude<R> const& a, LuMagnitude<R> const& b)
58{
59 if (b.scale == 0) return false;
60 if (a.scale <= b.scale) return (a.scale / b.scale) * a.unit < b.unit;
61 return a.unit < (b.scale / a.scale) * b.unit;
62}
63
64template <Real R> bool lu_small_pivot(LuMagnitude<R> const& pivot, LuMagnitude<R> const& scale, R const& tolerance)
65{
66 if (tolerance == 0) return false;
67 if (pivot.scale <= scale.scale) return (pivot.scale / scale.scale) * (pivot.unit / scale.unit) <= tolerance;
68 // Compare the reciprocal ratio when the pivot grew beyond the input scale.
69 // This avoids overflow in pivot.scale / scale.scale.
70 return scalar_like(tolerance, 1) / tolerance <= (scale.scale / pivot.scale) * (scale.unit / pivot.unit);
71}
72
73template <class Matrix, Real R> auto lu_input_scale(Matrix const& a, R const& zero)
74{
75 LuMagnitude<R> scale{zero, scalar_like(zero, 1)};
76 for (std::size_t j = 0; j < std::size_t(a.extent(1)); ++j)
77 for (std::size_t i = 0; i < std::size_t(a.extent(0)); ++i)
78 {
79 auto next = lu_magnitude(static_cast<typename Matrix::value_type>(a[i, j]));
80 if (lu_magnitude_less(scale, next)) scale = std::move(next);
81 }
82 return scale;
83}
84
85template <class Matrix, Real R>
86bool check_lu_diagonal(Matrix const& a, LuMagnitude<R> const& scale, R const& tolerance, SolveInfo& info)
87{
88 for (std::size_t k = 0; k < std::size_t(a.extent(0)); ++k)
89 {
90 auto magnitude = lu_magnitude(static_cast<typename Matrix::value_type>(a[k, k]));
91 if (magnitude.scale == 0)
92 info = {.status = SolveStatus::singular, .pivot = k};
93 else if (lu_small_pivot(magnitude, scale, tolerance))
94 info = {.status = SolveStatus::small_pivot, .pivot = k};
95 if (!info.succeeded()) return false;
96 }
97 return true;
98}
99
100// Direct dispatch is also callable without the owning-factor frontend. Shape
101// and pivot metadata enter here; established factors need no numerical rescan.
102template <class Matrix> void require_lu_shape(Matrix const& a, std::size_t pivot_count)
103{
104 ERROR_IF(a.extent(0) != a.extent(1), "LU requires a square matrix");
105 ERROR_IF(std::size_t(a.extent(0)) != pivot_count, "LU pivot count mismatch");
106}
107template <class Matrix, class Rhs>
108void require_lu_solve_shape(Matrix const& a, std::span<std::size_t const> pivots, Rhs const& b)
109{
110 require_lu_shape(a, pivots.size());
111 ERROR_IF(a.extent(0) != b.extent(0), "LU solve RHS row count mismatch");
112}
113} // namespace uni20::linalg::detail
locale_t value
Definition data_table.cpp:49
Definition qnum.hpp:403
void info(checked_format_string< Args... > format, Args &&... args)
Emit an informational display message.
Definition display.hpp:393
DiagnosticMagnitude< R > magnitude(R const &value)
Definition gmres_diagnostics.hpp:98
R ratio(DiagnosticMagnitude< R > const &a, DiagnosticMagnitude< R > const &b)
Definition gmres_diagnostics.hpp:125
@ singular
An exactly zero elimination pivot was found.
@ small_pivot
A nonzero pivot failed the requested relative threshold.
constexpr detail::scalar_math::sqrt_fn sqrt
Square root; exact inputs requiring approximation need an explicit finite precision.
Definition math.hpp:461
constexpr detail::scalar_math::abs_fn abs
Absolute value or complex magnitude, preserving the selected scalar implementation.
Definition math.hpp:459
void sum(BackendSelector selector, async::Async< OutputTensor > &output, async::Async< InputTensor > const &input)
Schedule a full sum into an explicit async scalar tensor.
Definition linalg/async/reductions.hpp:309
constexpr auto scalar_like(S const &exemplar, Value const &value) -> S
Construct a scalar using an exemplar's type and numerical precision.
Definition math.hpp:195
#define ERROR_IF(cond,...)
Definition trace.hpp:389