Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
backends/cpu/linear_solve.hpp
Go to the documentation of this file.
1#pragma once
2
9#include "lu_common.hpp"
20
21#include <cmath>
22#include <concepts>
23#include <cstddef>
24#include <type_traits>
25#include <utility>
26
27namespace uni20::linalg
28{
29namespace detail::cpu_reference
30{
31
32template <class CoefficientMdspan, class RhsMdspan> consteval auto linear_solve_acceptance()
33{
34 using coefficient_type = std::remove_cvref_t<CoefficientMdspan>;
35 using rhs_type = std::remove_cvref_t<RhsMdspan>;
36 using scalar_type = typename coefficient_type::value_type;
37 using coefficient_index = typename coefficient_type::index_type;
38 using rhs_index = typename rhs_type::index_type;
39
40 if constexpr (!uni20::has_runtime_precision_v<scalar_type> && uni20::RealOrComplex<scalar_type> &&
41 std::same_as<scalar_type, typename rhs_type::value_type> &&
42 requires(CoefficientMdspan& coefficients, RhsMdspan& rhs, coefficient_index coefficient_i,
43 rhs_index rhs_i, scalar_type value) {
44 static_cast<scalar_type>(coefficients[coefficient_i, coefficient_i]);
45 static_cast<scalar_type>(rhs[rhs_i, rhs_i]);
46 coefficients[coefficient_i, coefficient_i] = value;
47 rhs[rhs_i, rhs_i] = value;
48 value / value;
49 value -= value * value;
50 })
51 return kernel_types_yes;
52 else
53 return kernel_types_no;
54}
55
56template <class CoefficientMdspan, class RhsMdspan>
57KernelAttempt linear_solve(CoefficientMdspan& coefficients, RhsMdspan& right_hand_sides, SolveInfo& info,
59{
60 using coefficient_type = std::remove_cvref_t<CoefficientMdspan>;
61 using rhs_type = std::remove_cvref_t<RhsMdspan>;
62 using scalar_type = typename coefficient_type::value_type;
63 using real_type = uni20::make_real_t<scalar_type>;
64 using coefficient_index = typename coefficient_type::index_type;
65 using rhs_index = typename rhs_type::index_type;
66
67 CHECK_EQUAL(coefficients.extent(0), coefficients.extent(1));
68 CHECK_EQUAL(coefficients.extent(0), right_hand_sides.extent(0));
69 std::size_t const order = static_cast<std::size_t>(coefficients.extent(0));
70 std::size_t const rhs_count = static_cast<std::size_t>(right_hand_sides.extent(1));
71 // Direct mdspan dispatch bypasses the Tensor frontend's option validation.
72 require_solve_options(options);
73 info = {};
74 if (order == 0 || rhs_count == 0) return KernelAttempt::success;
75 real_type scale{};
76 if (!prepare_linear_solve(coefficients, right_hand_sides, scale, info)) return KernelAttempt::success;
77
78 using std::abs;
79 for (std::size_t k = 0; k < order; ++k)
80 {
81 auto const coefficient_k = static_cast<coefficient_index>(k);
82 std::size_t pivot_row = k;
83 real_type pivot_value = abs(static_cast<scalar_type>(coefficients[coefficient_k, coefficient_k]));
84 for (std::size_t row = k + 1; row < order; ++row)
85 {
86 auto const coefficient_row = static_cast<coefficient_index>(row);
87 real_type const candidate = abs(static_cast<scalar_type>(coefficients[coefficient_row, coefficient_k]));
88 if (candidate > pivot_value)
89 {
90 pivot_value = candidate;
91 pivot_row = row;
92 }
93 }
94
95 if (!check_solve_pivot(pivot_value, scale, options, k, info)) return KernelAttempt::success;
96 swap_rows(coefficients, k, pivot_row, order);
97 swap_rows(right_hand_sides, k, pivot_row, rhs_count);
98
99 scalar_type const pivot = static_cast<scalar_type>(coefficients[coefficient_k, coefficient_k]);
100 for (std::size_t row = k + 1; row < order; ++row)
101 {
102 auto const coefficient_row = static_cast<coefficient_index>(row);
103 scalar_type const factor = static_cast<scalar_type>(coefficients[coefficient_row, coefficient_k]) / pivot;
104 if (factor == scalar_type{}) continue;
105
106 coefficients[coefficient_row, coefficient_k] = scalar_type{};
107 for (std::size_t col = k + 1; col < order; ++col)
108 {
109 auto const coefficient_col = static_cast<coefficient_index>(col);
110 scalar_type value = static_cast<scalar_type>(coefficients[coefficient_row, coefficient_col]);
111 value -= factor * static_cast<scalar_type>(coefficients[coefficient_k, coefficient_col]);
113 {
116 }
117 coefficients[coefficient_row, coefficient_col] = value;
118 }
119 for (std::size_t col = 0; col < rhs_count; ++col)
120 {
121 auto const rhs_row = static_cast<rhs_index>(row);
122 auto const rhs_k = static_cast<rhs_index>(k);
123 auto const rhs_col = static_cast<rhs_index>(col);
124 scalar_type value = static_cast<scalar_type>(right_hand_sides[rhs_row, rhs_col]);
125 value -= factor * static_cast<scalar_type>(right_hand_sides[rhs_k, rhs_col]);
127 {
130 }
131 right_hand_sides[rhs_row, rhs_col] = value;
132 }
133 }
134 }
135
136 for (std::size_t row = order; row-- > 0;)
137 {
138 auto const coefficient_row = static_cast<coefficient_index>(row);
139 scalar_type const pivot = static_cast<scalar_type>(coefficients[coefficient_row, coefficient_row]);
140 for (std::size_t col = 0; col < rhs_count; ++col)
141 {
142 auto const rhs_row = static_cast<rhs_index>(row);
143 auto const rhs_col = static_cast<rhs_index>(col);
144 scalar_type value = static_cast<scalar_type>(right_hand_sides[rhs_row, rhs_col]);
145 for (std::size_t k = row + 1; k < order; ++k)
146 {
147 auto const coefficient_k = static_cast<coefficient_index>(k);
148 auto const rhs_k = static_cast<rhs_index>(k);
149 value -= static_cast<scalar_type>(coefficients[coefficient_row, coefficient_k]) *
150 static_cast<scalar_type>(right_hand_sides[rhs_k, rhs_col]);
151 }
152 value /= pivot;
154 {
157 }
158 right_hand_sides[rhs_row, rhs_col] = value;
159 }
160 }
161
163}
164
165} // namespace detail::cpu_reference
166
168template <uni20::MutableRankedMdspecLike<2> CoefficientMdspec, uni20::MutableRankedMdspecLike<2> RhsMdspec>
170consteval auto kernel_accepts_types(CpuReferenceBackend const&, linear_solve_op const&, CoefficientMdspec&, RhsMdspec&,
171 SolveInfo&,
173{
174 using coefficient_span = uni20::host_write_mdspan_t<CoefficientMdspec>;
176 constexpr auto acceptance = detail::cpu_reference::linear_solve_acceptance<coefficient_span, rhs_span>();
177 if constexpr (acceptance == KernelTypeAcceptance::yes)
178 return kernel_types_yes;
179 else
180 return kernel_types_no;
181}
182
184template <uni20::MutableRankedMdspecLike<2> CoefficientMdspec, uni20::MutableRankedMdspecLike<2> RhsMdspec>
186KernelAttempt try_kernel(CpuReferenceBackend, linear_solve_op const&, CoefficientMdspec& coefficients,
187 RhsMdspec& right_hand_sides, SolveInfo& info,
189{
190 auto coefficient_access = acquire_host_write_access_sync(coefficients);
191 auto rhs_access = acquire_host_write_access_sync(right_hand_sides);
192 auto coefficient_span = coefficient_access.mdspan();
193 auto rhs_span = rhs_access.mdspan();
194 return detail::cpu_reference::linear_solve(coefficient_span, rhs_span, info, options);
195}
196
198template <MutableRankedMdspecLike<2> A, MutableRankedMdspecLike<2> B>
200 has_runtime_precision_v<typename A::value_type> &&
201 std::same_as<typename A::value_type, typename B::value_type>
207
209template <MutableRankedMdspecLike<2> A, MutableRankedMdspecLike<2> B>
211 has_runtime_precision_v<typename A::value_type> &&
212 std::same_as<typename A::value_type, typename B::value_type>
215{
217 (void)p.bit_count();
218 ERROR_IF(a.extent(0) != a.extent(1) || a.extent(0) != b.extent(0), "solve operand shapes do not agree");
219 detail::require_solve_options(options);
220 info = {};
221 if (a.extent(0) == 0 || b.extent(1) == 0) return KernelAttempt::success;
224 auto as = aa.mdspan();
225 auto bs = ba.mdspan();
226 if (!detail::solve_matrix_is_finite(as) || !detail::solve_matrix_is_finite(bs))
227 {
228 info.status = SolveStatus::nonfinite_input;
230 }
231 auto av = packed_detail::pack(as, p), bv = packed_detail::pack(bs, p);
232 auto aw = av.mdspan();
233 auto bw = bv.mdspan();
234 if (!detail::solve_matrix_is_finite(aw) || !detail::solve_matrix_is_finite(bw))
235 {
236 info.status = SolveStatus::nonfinite_result;
238 }
239 SolveOptions<R> converted{.relative_pivot_tolerance = scalar_at_precision<R>(options.relative_pivot_tolerance, p)};
240 std::vector<std::size_t> pivots(std::size_t(a.extent(0)));
241 detail::cpu_reference::lu_factor(aw, std::span(pivots), nullptr, info, converted, scalar_at_precision<R>(0, p));
242 packed_detail::unpack(av, as);
243 if (info.succeeded())
244 {
245 detail::cpu_reference::lu_solve(aw, std::span<std::size_t const>(pivots), bw, info);
246 packed_detail::unpack(bv, bs);
247 }
249}
250
251} // 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
Mutable mdspec metadata supporting host write acquisition.
Definition access.hpp:694
Concept for types that are either real or complex.
Definition scalar_concepts.hpp:87
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
Mdspan concept and accessor extensions for Uni20.
void info(checked_format_string< Args... > format, Args &&... args)
Emit an informational display message.
Definition display.hpp:393
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
Definition linalg/async.hpp:28
KernelAttempt try_kernel(BlasBackend, gemm_op const &, C &c, S const &alpha, A &a, B &b, S const &beta)
Pack logical host values and execute GEMM through value-preserving provider conversion.
Definition linalg/backends/blas/binary80.hpp:30
KernelAttempt
Result of one runtime kernel backend attempt.
Definition kernel_attempt.hpp:27
@ 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.
constexpr KernelAcceptance< KernelTypeAcceptance::no > kernel_types_no
Report that a backend rejects the argument types.
Definition dispatch_error.hpp:34
consteval auto kernel_accepts_types(BlasBackend, gemm_op const &, C &, S const &, A &, B &, S const &)
Accept host-readable native binary80 operands, including transforming accessors.
Definition linalg/backends/blas/binary80.hpp:19
constexpr KernelAcceptance< KernelTypeAcceptance::yes > kernel_types_yes
Report that a backend accepts every instance of the argument types.
Definition dispatch_error.hpp:40
Definition data_table.cpp:8
constexpr auto abs(basic_half_int< T > value) -> basic_half_int< T >
Return the absolute value of a half-integer.
Definition half_int.hpp:431
std::remove_cvref_t< decltype(std::declval< detail::host_write_mdspan_lease_t< Mdspec > & >().mdspan())> host_write_mdspan_t
Writable mdspan type resolved by host descriptor acquisition.
Definition access.hpp:684
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.
Backend value for the reference host CPU dense linalg oracle.
Definition backend_selector.hpp:55
Diagnostics written by a completed numerical attempt, independent of dispatch.
Definition solve_info.hpp:41
Real-scalar pivot policy for a square linear solve.
Definition solve_info.hpp:62
Real relative_pivot_tolerance
Reject nonzero pivots at or below this fraction of the original maximum entry magnitude.
Definition solve_info.hpp:64
Destructive dense general linear-system solve operation tag.
Definition operation_tags.hpp:155
Concepts for tensor-level objects that resolve mdspan kernel operands.
#define CHECK_EQUAL(a, b,...)
Definition trace.hpp:309
#define ERROR_IF(cond,...)
Definition trace.hpp:389