Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
least_squares_common.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <span>
4#include <uni20/core/math.hpp>
10
12{
13template <class S, class... P>
14concept PrecisionArgs = (has_runtime_precision_v<S> && sizeof...(P) == 1 && (std::same_as<P, Precision> && ...)) ||
15 (!has_runtime_precision_v<S> && sizeof...(P) == 0);
16template <class S> Precision working_precision() { return native_precision<S>(); }
17template <class S> Precision working_precision(Precision p) { return p; }
18
19template <class X, class A, class B>
20concept Operands =
23 std::same_as<typename X::value_type, typename A::value_type> &&
24 std::same_as<typename B::value_type, typename A::value_type>;
25
26template <Real R> void require_options(LeastSquaresOptions<R> const& options)
27{
29 "least squares requires a finite nonnegative diagonal tolerance");
30}
31template <class Matrix> bool finite(Matrix const& a)
32{
33 for (std::size_t j = 0; j < std::size_t(a.extent(1)); ++j)
34 for (std::size_t i = 0; i < std::size_t(a.extent(0)); ++i)
35 if (!uni20::isfinite(typename Matrix::value_type(a[i, j]))) return false;
36 return true;
37}
38
39template <class Matrix, Real R>
40bool check_diagonal(Matrix const& a, R const& scale, R const& tolerance, LeastSquaresInfo& info)
41{
42 for (std::size_t k = 0; k < std::size_t(a.extent(1)); ++k)
43 {
44 R diagonal = math::abs(a[k, k]);
45 if (!uni20::isfinite(diagonal))
47 else if (diagonal == 0)
48 info = {.status = LeastSquaresStatus::zero_diagonal, .column = k};
49 else if (tolerance != 0 && diagonal / scale <= tolerance)
50 info = {.status = LeastSquaresStatus::small_diagonal, .column = k};
51 if (!info.succeeded()) return false;
52 }
53 return true;
54}
55
56// Recompute against the original converted inputs, independently of factorization.
57template <class S>
58bool residual_norms(DenseMatrix<S> const& av, DenseMatrix<S> const& bv, DenseMatrix<S> const& solution,
59 std::vector<make_real_t<S>>& norms, Precision p)
60{
61 using R = make_real_t<S>;
62 std::size_t m = av.extent(0), n = av.extent(1), nrhs = bv.extent(1);
63 auto zero = scalar_at_precision<R>(0, p);
64 norms.assign(nrhs, zero);
65 // Reuse one RHS-sized scratch vector while accumulating along contiguous columns.
66 std::vector<S> error(m);
67 for (std::size_t j = 0; j < nrhs; ++j)
68 {
69 for (std::size_t i = 0; i < m; ++i)
70 error[i] = -bv[i, j];
71 for (std::size_t k = 0; k < n; ++k)
72 {
73 S const& value = solution[k, j];
74 for (std::size_t i = 0; i < m; ++i)
75 error[i] += av[i, k] * value;
76 }
77 for (std::size_t i = 0; i < m; ++i)
78 {
79 norms[j] = math::hypot(norms[j], math::abs(error[i]));
80 if (!uni20::isfinite(norms[j]))
81 {
82 return false;
83 }
84 }
85 }
86 return true;
87}
88
89// Both implementations transform owned A and B into an upper triangular system.
90// Conversion, checks, back substitution and residual reporting are shared.
91template <class X, class A, class B, class Factor>
92KernelAttempt run(X& x, A& a, B& b, std::span<make_real_t<typename A::value_type>> residuals, LeastSquaresInfo& info,
94{
95 using S = typename A::value_type;
96 using R = make_real_t<S>;
97 require_precision<S>(p);
98 (void)p.bit_count();
99 require_options(options);
100 std::size_t m = a.extent(0), n = a.extent(1), nrhs = b.extent(1);
101 ERROR_IF(m < n || std::size_t(b.extent(0)) != m || std::size_t(x.extent(0)) != n ||
102 std::size_t(x.extent(1)) != nrhs || residuals.size() != nrhs,
103 "incompatible full-rank least-squares shapes");
104 info = {};
105 if (nrhs == 0) return KernelAttempt::success;
108 auto as = aa.mdspan();
109 auto bs = ba.mdspan();
110 if (!finite(as) || !finite(bs))
111 {
114 }
115 auto av = packed_detail::pack(as, p);
116 auto bv = packed_detail::pack(bs, p);
117 auto tolerance = scalar_at_precision<R>(options.relative_diagonal_tolerance, p);
118 if (!finite(av.mdspan()) || !finite(bv.mdspan()) || !uni20::isfinite(tolerance))
119 {
122 }
123 auto zero = scalar_at_precision<R>(0, p);
124 R scale = zero;
125 for (std::size_t j = 0; j < n; ++j)
126 for (std::size_t i = 0; i < m; ++i)
127 scale = std::max(scale, R(math::abs(av[i, j])));
129 {
132 }
133 auto work = av;
134 auto rhs = bv;
135 if (n != 0)
136 {
137 factor(work, rhs, p);
138 if (!finite(work.mdspan()) || !finite(rhs.mdspan()))
139 {
142 }
143 if (!check_diagonal(work, scale, tolerance, info)) return KernelAttempt::success;
144 }
145 DenseMatrix<S> solution(uninitialized, n, nrhs, p);
146 // Packing gives both backends column-major workspaces, irrespective of input layout.
147 for (std::size_t j = 0; j < nrhs; ++j)
148 {
149 for (std::size_t i = 0; i < n; ++i)
150 solution[i, j] = rhs[i, j];
151 for (std::size_t k = n; k-- > 0;)
152 {
153 solution[k, j] /= work[k, k];
154 S const& value = solution[k, j];
155 for (std::size_t i = 0; i < k; ++i)
156 solution[i, j] -= work[i, k] * value;
157 }
158 }
159 if (!finite(solution.mdspan()))
160 {
163 }
164 std::vector<R> norms;
165 if (!residual_norms(av, bv, solution, norms, p))
166 {
169 }
171 auto xs = xa.mdspan();
172 packed_detail::unpack(solution, xs);
173 std::copy_n(norms.begin(), nrhs, residuals.begin());
175}
176} // namespace uni20::linalg::least_squares_detail
RAII leases that resolve device descriptors and tensor views to mdspans.
Exact arithmetic or an explicit binary working precision.
Definition core/precision.hpp:13
General-purpose owning tensor with immediate or deferred mdspan metadata.
Definition basic_tensor.hpp:109
auto extent(size_type axis) const noexcept
Return one tensor extent.
Definition basic_tensor.hpp:720
auto mdspan() noexcept -> mdspan_type
Resolve a writable mdspan over the owned storage.
Definition basic_tensor.hpp:609
Mdspec metadata supporting host read acquisition.
Definition access.hpp:688
Mutable mdspec metadata supporting host write acquisition.
Definition access.hpp:694
Mutable mdspec-like type with a specified static rank.
Definition mdspan/concepts.hpp:487
An mdspec-like type with a specified static rank.
Definition mdspan/concepts.hpp:454
Concept for types that are either real or complex.
Definition scalar_concepts.hpp:87
Definition least_squares_common.hpp:20
Definition least_squares_common.hpp:14
locale_t value
Definition data_table.cpp:49
Operation-tag backend dispatch over tensor-view operands.
typename make_real< T >::type make_real_t
Alias for the underlying real-valued type of T.
Definition scalar_traits.hpp:168
constexpr bool isfinite(I const &x) noexcept
Returns whether an integer scalar is finite.
Definition math.hpp:121
Scalar math helper utilities.
Definition backends/cpu/least_squares.hpp:8
Precision working_precision()
Definition least_squares_common.hpp:16
bool residual_norms(DenseMatrix< S > const &av, DenseMatrix< S > const &bv, DenseMatrix< S > const &solution, std::vector< make_real_t< S > > &norms, Precision p)
Definition least_squares_common.hpp:58
bool finite(Matrix const &a)
Definition least_squares_common.hpp:31
KernelAttempt run(X &x, A &a, B &b, std::span< make_real_t< typename A::value_type > > residuals, LeastSquaresInfo &info, LeastSquaresOptions< make_real_t< typename A::value_type > > const &options, Precision p, Factor factor)
Definition least_squares_common.hpp:92
void require_options(LeastSquaresOptions< R > const &options)
Definition least_squares_common.hpp:26
bool check_diagonal(Matrix const &a, R const &scale, R const &tolerance, LeastSquaresInfo &info)
Definition least_squares_common.hpp:40
void unpack(Tensor const &source, Span &target)
Definition packed_matrix.hpp:33
auto pack(Span const &span, Precision p, bool read=true)
Definition packed_matrix.hpp:19
KernelAttempt
Result of one runtime kernel backend attempt.
Definition kernel_attempt.hpp:27
constexpr detail::scalar_math::abs_fn abs
Absolute value or complex magnitude, preserving the selected scalar implementation.
Definition math.hpp:459
constexpr detail::scalar_math::hypot_fn hypot
Euclidean length with scalar-specific overflow and underflow handling.
Definition math.hpp:507
Definition data_table.cpp:8
constexpr uninitialized_t uninitialized
Explicit allocation tag for overwrite outputs and scratch space.
Definition common/initialization.hpp:22
auto acquire_host_read_access_sync(Mdspan const &mdspan)
Acquire a directly host-accessible read-only mdspan.
Definition access.hpp:616
auto acquire_host_write_access_sync(Mdspan &mdspan)
Acquire a directly host-accessible writable mdspan.
Definition access.hpp:626
Backend-independent operation values used with normalized kernel operands.
Logical host packing for value-converting BLAS and LAPACK adapters.
Completed numerical attempt; success does not certify rank or conditioning.
Definition least_squares_info.hpp:26
Full-rank QR rejection policy, independent of any physical convergence criterion.
Definition least_squares_info.hpp:37
R relative_diagonal_tolerance
Reject nonzero abs(R[k,k])/max(abs(A)) at or below this finite, nonnegative tolerance.
Definition least_squares_info.hpp:41
Multiply one element value by a retained scalar factor.
Definition elementwise_functions.hpp:68
#define ERROR_IF(cond,...)
Definition trace.hpp:389