Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
solve_diagnostics.hpp
Go to the documentation of this file.
1#pragma once
2
8#include <algorithm>
9#include <uni20/core/math.hpp>
13#include <vector>
14
15namespace uni20::linalg
16{
18template <RealOrComplex S> struct SolveDiagnosticsResult
19{
21 std::optional<DenseMatrix<S>> residual = {};
22 std::vector<SolveError<make_real_t<S>>> columns = {};
23};
24
25namespace refinement_detail
26{
27template <class T>
28concept HostMatrix =
30
31template <RealOrComplex To, RealOrComplex From>
32 requires(Complex<To> == Complex<From>)
33To convert(From const& value, Precision p)
34{
35 if constexpr (Complex<To>)
36 {
37 using R = make_real_t<To>;
38 return To(convert<R>(value.real(), p), convert<R>(value.imag(), p));
39 }
40 else if constexpr (has_runtime_precision_v<To>)
41 return scalar_at_precision<To>(value, p);
42 else
43 {
44 require_precision<To>(p);
45 return static_cast<To>(value);
46 }
47}
48
49// Conversion is an explicit algorithm workspace boundary. Observe logical
50// accessor values while the host access lease is held, never raw storage.
51template <RealOrComplex To, HostMatrix A>
53std::optional<DenseMatrix<To>> pack(A const& input, Precision p, SolveInfo& info)
54{
55 using From = tensor_element_t<A>;
56 auto descriptor = mdspec_of(input);
57 auto access = acquire_host_read_access_sync(descriptor);
58 auto values = access.mdspan();
59 DenseMatrix<To> output(uninitialized, input.extent(0), input.extent(1), p);
60 for (std::size_t j = 0; j < std::size_t(input.extent(1)); ++j)
61 for (std::size_t i = 0; i < std::size_t(input.extent(0)); ++i)
62 {
63 From value(values[i, j]);
65 {
66 info.status = SolveStatus::nonfinite_input;
67 return {};
68 }
69 auto converted = convert<To>(value, p);
70 if (!uni20::isfinite(converted))
71 {
73 return {};
74 }
75 output[i, j] = std::move(converted);
76 }
77 return output;
78}
79
80template <RealOrComplex S>
83{
84 using R = make_real_t<S>;
85 auto const zero = scalar_at_precision<R>(0, p);
86 auto const szero = scalar_at_precision<S>(0, p);
87 std::size_t const n = a.extent(0), count = b.extent(1);
89 DenseMatrix<S> residual(n, count, p);
90 std::vector<R> row_sums(n, zero);
91 R a_norm = zero;
92 for (std::size_t k = 0; k < n; ++k)
93 for (std::size_t i = 0; i < n; ++i)
94 row_sums[i] += math::abs(a[i, k]);
95 for (auto const& sum : row_sums)
96 a_norm = std::max(a_norm, sum);
97 if (!uni20::isfinite(a_norm))
98 {
100 return result;
101 }
102 for (std::size_t j = 0; j < count; ++j)
103 {
104 R x_norm = zero, b_norm = zero, r_norm = zero, componentwise = zero;
105 std::vector<R> denominator(n, zero);
106 for (std::size_t i = 0; i < n; ++i)
107 {
108 residual[i, j] = b[i, j];
109 denominator[i] = math::abs(b[i, j]);
110 b_norm = std::max(b_norm, denominator[i]);
111 x_norm = std::max(x_norm, R(math::abs(x[i, j])));
112 }
113 // Column traversal follows the packed matrix layout. All products and
114 // sums are at residual precision, including the converted iterate.
115 for (std::size_t k = 0; k < n; ++k)
116 {
117 R x_magnitude = math::abs(x[k, j]);
118 for (std::size_t i = 0; i < n; ++i)
119 {
120 residual[i, j] -= a[i, k] * x[k, j];
121 denominator[i] += math::abs(a[i, k]) * x_magnitude;
122 }
123 }
124 for (std::size_t i = 0; i < n; ++i)
125 {
126 R magnitude = math::abs(residual[i, j]);
127 if (!uni20::isfinite(magnitude) || !uni20::isfinite(denominator[i]))
128 {
130 return result;
131 }
132 r_norm = std::max(r_norm, magnitude);
133 // A zero denominator means an identically zero row contribution, so its
134 // residual is zero as well. Define that 0/0 contribution to be zero.
135 R ratio = denominator[i] == 0 ? zero : R(magnitude / denominator[i]);
136 if (denominator[i] == 0 && residual[i, j] != szero)
137 {
139 return result;
140 }
141 componentwise = std::max(componentwise, ratio);
142 }
143 R denominator_norm = a_norm * x_norm + b_norm;
144 if (!uni20::isfinite(denominator_norm))
145 {
147 return result;
148 }
149 R normwise = denominator_norm == 0 ? zero : R(r_norm / denominator_norm);
150 result.columns.push_back({r_norm, normwise, componentwise});
151 }
152 result.residual.emplace(std::move(residual));
153 return result;
154}
155} // namespace refinement_detail
156
166template <RealOrComplex S, refinement_detail::HostMatrix A, refinement_detail::HostMatrix B,
167 refinement_detail::HostMatrix X>
170[[nodiscard]] SolveDiagnosticsResult<S> solve_diagnostics_with_info(A const& a, B const& b, X const& x, Precision p)
171{
172 require_precision<S>(p);
173 (void)p.bit_count();
174 ERROR_IF(a.extent(0) != a.extent(1) || b.extent(0) != a.extent(0) || x.extent(0) != a.extent(1) ||
175 x.extent(1) != b.extent(1),
176 "solve diagnostics require square A and matching RHS/solution shapes");
178 if (a.extent(0) == 0 || b.extent(1) == 0)
179 {
180 auto zero = scalar_at_precision<make_real_t<S>>(0, p);
181 result.residual.emplace(a.extent(0), b.extent(1), p);
182 result.columns.assign(b.extent(1), {zero, zero, zero});
183 return result;
184 }
185 auto av = refinement_detail::pack<S>(a, p, result.info);
186 if (!av) return result;
187 auto bv = refinement_detail::pack<S>(b, p, result.info);
188 if (!bv) return result;
189 auto xv = refinement_detail::pack<S>(x, p, result.info);
190 if (!xv) return result;
191 result = refinement_detail::evaluate(*av, *bv, *xv, p);
192 if (!result.info.succeeded()) result.columns.clear();
193 return result;
194}
195} // namespace uni20::linalg
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
Concept for complex scalar types.
Definition scalar_concepts.hpp:75
Mdspec metadata supporting host read acquisition.
Definition access.hpp:688
TensorView whose readable mdspec has a specified static rank.
Definition tensor/concepts.hpp:149
Concept for types that are either real or complex.
Definition scalar_concepts.hpp:87
Definition solve_diagnostics.hpp:28
locale_t value
Definition data_table.cpp:49
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.
SolveDiagnosticsResult< S > evaluate(DenseMatrix< S > const &a, DenseMatrix< S > const &b, DenseMatrix< S > const &x, Precision p)
Definition solve_diagnostics.hpp:81
To convert(From const &value, Precision p)
Definition solve_diagnostics.hpp:33
Definition linalg/async.hpp:28
@ nonfinite_input
An input coefficient or RHS component was nonfinite; both workspaces are preserved.
@ nonfinite_result
A factor, solution, reduction, or required magnitude became nonfinite.
SolveDiagnosticsResult< S > solve_diagnostics_with_info(A const &a, B const &b, X const &x, Precision p)
Evaluate b - A*x and normwise/componentwise backward error in scalar S at p.
Definition solve_diagnostics.hpp:170
constexpr detail::scalar_math::abs_fn abs
Absolute value or complex magnitude, preserving the selected scalar implementation.
Definition math.hpp:459
Definition data_table.cpp:8
constexpr uninitialized_t uninitialized
Explicit allocation tag for overwrite outputs and scratch space.
Definition common/initialization.hpp:22
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
auto acquire_host_read_access_sync(Mdspan const &mdspan)
Acquire a directly host-accessible read-only mdspan.
Definition access.hpp:616
constexpr decltype(auto) mdspec_of(Tensor &tensor) noexcept(noexcept(tensor.mdspec()))
Return a tensor's immediate or descriptor-backed multidimensional metadata.
Definition tensor/concepts.hpp:50
std::remove_cv_t< typename tensor_mdspec_t< T >::element_type > tensor_element_t
Scalar value type exposed by a tensor-level object.
Definition tensor/concepts.hpp:126
Recomputed residual and per-RHS diagnostics, published together only on success.
Definition solve_diagnostics.hpp:19
SolveInfo info
Definition solve_diagnostics.hpp:20
std::optional< DenseMatrix< S > > residual
Definition solve_diagnostics.hpp:21
std::vector< SolveError< make_real_t< S > > > columns
Definition solve_diagnostics.hpp:22
Diagnostics written by a completed numerical attempt, independent of dispatch.
Definition solve_info.hpp:41
SolveStatus status
Definition solve_info.hpp:42
bool succeeded() const noexcept
Report successful completion of the numerical operation.
Definition solve_info.hpp:49
General-purpose owning tensors with runtime extents.
#define ERROR_IF(cond,...)
Definition trace.hpp:389