22 std::vector<RefinementColumn<make_real_t<R>>>
columns = {};
25namespace refinement_detail
27template <
class F,
class W,
class R>
30 require_precision<F>(
p.factor);
31 require_precision<W>(
p.working);
32 require_precision<R>(
p.residual);
33 (void)
p.factor.bit_count();
34 (void)
p.working.bit_count();
35 (void)
p.residual.bit_count();
37 if (options.backward_error_tolerance)
39 "refinement requires a finite nonnegative backward-error tolerance");
40 RR tolerance = options.backward_error_tolerance
41 ? convert<RR>(*options.backward_error_tolerance,
p.residual)
53template <
class W,
class R,
class A,
class B>
57 result.
solution.emplace(a.extent(1), b.extent(1), working);
58 auto zero = scalar_at_precision<make_real_t<R>>(0, residual);
59 result.
columns.assign(b.extent(1), {.error = {zero, zero, zero}, .converged =
true});
74template <RealOrComplex To, RealOrComplex From>
78 if (exponent == 0)
return convert<To>(
value,
p);
81 convert_scaled<make_real_t<To>>(
value.imag(), exponent,
p));
85 return math::ldexp(convert<To>(parts.fraction,
p), parts.exponent + exponent);
92template <
class Backend,
class F,
class W,
class R>
95 std::int64_t target_exponent,
SolveInfo& info)
98 auto zero = scalar_at_precision<RR>(0,
p.residual);
101 std::vector<std::int64_t> shifts(count, 0);
102 for (std::size_t j = 0; j < count; ++j)
104 if (!active[j])
continue;
106 for (std::size_t i = 0; i < n; ++i)
108 if (magnitude == 0)
continue;
109 shifts[j] = target_exponent -
math::frexp(magnitude).exponent;
110 for (std::size_t i = 0; i < n; ++i)
111 work[i, j] = convert_scaled<F>(R(rhs[i, j]), shifts[j],
p.factor);
114 if (!info.succeeded())
return {};
116 for (std::size_t j = 0; j < count; ++j)
118 if (!active[j])
continue;
119 for (std::size_t i = 0; i < n; ++i)
121 R scaled = convert_scaled<R>(F(work[i, j]), -shifts[j],
p.residual);
122 W
value = convert<W>(scaled,
p.working);
128 result[i, j] = std::move(
value);
134template <
class Backend,
class F,
class W,
class R>
142 std::vector<bool> active(count,
true);
143 auto upper_scale = scalar_at_precision<make_real_t<F>>(0,
p.factor);
145 for (std::size_t j = 0; j < n; ++j)
146 for (std::size_t i = 0; i <= j; ++i)
149 upper_scale = std::max(upper_scale, magnitude);
150 if (i == j) pivot_scale = std::min(pivot_scale, magnitude);
152 auto target_exponent = std::min(std::int64_t{0},
math::frexp(upper_scale).exponent);
159 auto pivot_exponent =
math::frexp(pivot_scale).exponent;
161 target_exponent = std::min(target_exponent, pivot_exponent + headroom);
163 auto initial =
correction(backend, factor, b,
p, active, target_exponent, solve_info);
169 auto rx = pack<R>(*initial,
p.residual, solve_info);
175 auto evaluated =
evaluate(a, b, *rx,
p.residual);
176 if (!evaluated.info.succeeded())
181 result.
solution.emplace(std::move(*initial));
182 for (
auto const& error : evaluated.columns)
183 result.
columns.push_back({.error = error, .converged = error.componentwise_backward_error <= tolerance});
185 for (std::size_t step = 0;; ++step)
187 bool all_converged =
true, any_active =
false;
188 for (std::size_t j = 0; j < count; ++j)
190 all_converged = all_converged && result.columns[j].converged;
191 active[j] = !result.columns[j].converged && !result.columns[j].stagnated;
192 any_active = any_active || active[j];
194 if (all_converged)
return result;
195 if (!any_active || step == options.max_iterations)
200 auto delta =
correction(backend, factor, *evaluated.residual,
p, active, target_exponent, solve_info);
209 auto next = pack<W>(*result.solution,
p.working, solve_info);
210 std::vector<bool> changed(count,
false);
211 for (std::size_t j = 0; j <
count; ++j)
213 for (std::size_t i = 0; i < n; ++i)
215 W
value = (*next)[i, j] + (*delta)[i, j];
221 changed[j] = changed[j] ||
value != (*next)[i, j];
222 (*next)[i, j] = std::move(
value);
224 auto next_r = pack<R>(*next,
p.residual, solve_info);
230 auto next_evaluated =
evaluate(a, b, *next_r,
p.residual);
231 if (!next_evaluated.info.succeeded())
236 result.solution.emplace(std::move(*next));
237 evaluated = std::move(next_evaluated);
238 for (std::size_t j = 0; j <
count; ++j)
240 auto& column = result.columns[j];
241 column.error = evaluated.columns[j];
242 column.converged = column.error.componentwise_backward_error <= tolerance;
245 ++column.corrections;
246 column.stagnated = !column.converged && !changed[j];
254 ERROR_IF(a.extent(0) != a.extent(1) || a.extent(0) != b.extent(0),
255 "LU refinement requires square A and matching RHS rows");
271 refinement_detail::HostMatrix A, refinement_detail::HostMatrix B>
277 auto tolerance = refinement_detail::validate(
p, options);
278 refinement_detail::require_shape(a, b);
280 "refinement factors must match the matrix order and factor precision");
281 if (a.extent(0) == 0 || b.extent(1) == 0)
return refinement_detail::empty_result<W, R>(a, b,
p.working,
p.residual);
282 LuRefinementResult<W, R> result;
284 auto av = refinement_detail::pack<R>(a,
p.residual, info);
285 auto bv = av ? refinement_detail::pack<R>(b,
p.residual, info) :
std::nullopt;
288 result.info = refinement_detail::conversion_failure(info);
291 return refinement_detail::run(backend, factor, *av, *bv,
p, options, tolerance);
301template <KernelBackendSelector Backend, RealOrComplex F, RealOrComplex W, RealOrComplex R,
302 refinement_detail::HostMatrix A, refinement_detail::HostMatrix B>
303 requires(Complex<F> == Complex<tensor_element_t<A>> && Complex<F> == Complex<tensor_element_t<B>>)
306 LuOptions<make_real_t<F>>
const& factor_options = {.retain_norms =
false})
308 auto tolerance = refinement_detail::validate(
p, options);
309 refinement_detail::require_shape(a, b);
310 detail::require_solve_options(SolveOptions<make_real_t<F>>{factor_options.relative_pivot_tolerance});
311 if (a.extent(0) == 0 || b.extent(1) == 0)
return refinement_detail::empty_result<W, R>(a, b,
p.working,
p.residual);
312 LuRefinementResult<W, R> result;
314 auto av = refinement_detail::pack<R>(a,
p.residual, info);
315 auto bv = av ? refinement_detail::pack<R>(b,
p.residual, info) :
std::nullopt;
316 auto af = bv ? refinement_detail::pack<F>(a,
p.factor, info) :
std::nullopt;
317 if (!av || !bv || !af)
319 result.info = refinement_detail::conversion_failure(info);
323 if (!factor.info.succeeded())
325 result.info = {.status = RefinementStatus::factorization_failed, .solve_info = factor.info};
328 return refinement_detail::run(backend, *factor.factor, *av, *bv,
p, options, tolerance);
332template <RealOrComplex F, RealOrComplex W, RealOrComplex R, refinement_detail::HostMatrix A,
333 refinement_detail::HostMatrix B>
334 requires(Complex<F> == Complex<tensor_element_t<A>> && Complex<F> == Complex<tensor_element_t<B>>)
337 LuOptions<make_real_t<F>>
const& factor_options = {.retain_norms =
false})
339 return lu_refine_with_info(select_backend_for<DenseMatrix<F>>(lu_factor_op{}), a, b,
p, options, factor_options);
343template <RealOrComplex F, RealOrComplex W, RealOrComplex R, refinement_detail::HostMatrix A,
344 refinement_detail::HostMatrix B>
345 requires(Complex<F> == Complex<tensor_element_t<A>> && Complex<F> == Complex<tensor_element_t<B>>)
350 return lu_refine_with_info(select_backend_for<
DenseMatrix<F>>(lu_solve_op{}), factor, a, b,
p, options);
354template <
class... Args>
359 ERROR_IF(!result.info.succeeded(),
"LU refinement failed",
static_cast<int>(result.info.status));
360 return std::move(*result.solution);
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
Immutable owning packed factors satisfying P A = L U.
Definition ops/lu.hpp:34
Precision precision() const
Finite working precision used for these factors and subsequent solves.
Definition ops/lu.hpp:44
DenseMatrix< S > const & packed() const &noexcept
Read-only packed L/U matrix, valid while this object retains its storage.
Definition ops/lu.hpp:40
std::size_t order() const noexcept
Matrix order, including zero for a successfully factored empty matrix.
Definition ops/lu.hpp:38
Concept for complex scalar types.
Definition scalar_concepts.hpp:75
Concept for types that are either real or complex.
Definition scalar_concepts.hpp:87
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
void info(checked_format_string< Args... > format, Args &&... args)
Emit an informational display message.
Definition display.hpp:393
auto validate(LuRefinementPrecision< F, W, R > p, RefinementOptions< make_real_t< R > > const &options)
Definition lu_refine.hpp:28
SolveDiagnosticsResult< S > evaluate(DenseMatrix< S > const &a, DenseMatrix< S > const &b, DenseMatrix< S > const &x, Precision p)
Definition solve_diagnostics.hpp:81
void require_shape(A const &a, B const &b)
Definition lu_refine.hpp:252
LuRefinementResult< W, R > run(Backend &backend, LuFactorization< F > const &factor, DenseMatrix< R > const &a, DenseMatrix< R > const &b, LuRefinementPrecision< F, W, R > p, RefinementOptions< make_real_t< R > > const &options, make_real_t< R > const &tolerance)
Definition lu_refine.hpp:135
std::optional< DenseMatrix< W > > correction(Backend &backend, LuFactorization< F > const &factor, DenseMatrix< R > const &rhs, LuRefinementPrecision< F, W, R > p, std::vector< bool > const &active, std::int64_t target_exponent, SolveInfo &info)
Definition lu_refine.hpp:93
LuRefinementResult< W, R > empty_result(A const &a, B const &b, Precision working, Precision residual)
Definition lu_refine.hpp:54
auto component_magnitude(S const &value)
Definition lu_refine.hpp:63
To convert_scaled(From const &value, std::int64_t exponent, Precision p)
Definition lu_refine.hpp:76
RefinementInfo conversion_failure(SolveInfo const &info)
Definition lu_refine.hpp:47
Definition linalg/async.hpp:28
SolveInfo lu_solve_inplace_with_info(Backend &&backend, LuFactorization< S > const &factor, B &&b)
Solve in an existing RHS workspace, preserving the factors on every outcome.
Definition ops/lu.hpp:169
auto lu_refine(Args &&... args)
Return the refined solution only on convergence; other outcomes use the terminal error policy.
Definition lu_refine.hpp:356
@ 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.
auto lu_refine_with_info(Backend &&backend, LuFactorization< F > const &factor, A const &a, B const &b, LuRefinementPrecision< F, W, R > p, RefinementOptions< make_real_t< R > > const &options={})
Refine a preserving solve using existing LU factors and the original A and B.
Definition lu_refine.hpp:273
auto lu_factor_with_info(Backend &&backend, A const &a, Precision p, LuOptions< make_real_t< tensor_element_t< A > > > const &options={})
Factor at explicit finite precision, preserving input values and defaults.
Definition ops/lu.hpp:114
constexpr detail::scalar_math::ldexp_fn ldexp
Multiply by a power of two using the scalar's exponent interface.
Definition math.hpp:511
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::frexp_fn frexp
Binary fraction and exponent as an owning result.
Definition math.hpp:535
Definition data_table.cpp:8
mpreal epsilon(Precision precision)
Spacing above one for a specified binary working precision.
Definition mpreal.hpp:805
Owning reusable host LU factors and solves through operation dispatch.
Preserving host residual and backward-error evaluation at explicit precision.
Scalar families and explicit precisions for three-precision LU refinement.
Definition refinement_info.hpp:15
Refinement outcome, retaining the last finite, diagnosed iterate on later failure.
Definition lu_refine.hpp:19
RefinementInfo info
Definition lu_refine.hpp:20
std::optional< DenseMatrix< W > > solution
Definition lu_refine.hpp:21
std::vector< RefinementColumn< make_real_t< R > > > columns
Definition lu_refine.hpp:22
Outer-loop outcome and, on a factor/solve failure, the provider's numerical diagnostics.
Definition refinement_info.hpp:53
RefinementStatus status
Definition refinement_info.hpp:54
Stopping policy; tolerance is expressed in the residual real scalar.
Definition refinement_info.hpp:61
Diagnostics written by a completed numerical attempt, independent of dispatch.
Definition solve_info.hpp:41
Project-level numeric limits customization point.
Definition numeric_limits.hpp:16
#define ERROR_IF(cond,...)
Definition trace.hpp:389