Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
slogdet.hpp
Go to the documentation of this file.
1#pragma once
2
8#include "lu.hpp"
9
10namespace uni20::linalg
11{
15template <RealOrComplex S> struct LogDeterminant
16{
19};
20
24template <RealOrComplex S> struct LogDeterminantResult
25{
27 std::optional<LogDeterminant<S>> value;
28};
29
30namespace detail
31{
32template <RealOrComplex S> LogDeterminant<S> zero_determinant(Precision p)
33{
34 auto zero = scalar_at_precision<S>(0, p);
35 return {.phase = zero, .log_absolute = math::log(uni20::real(zero))};
36}
37
38template <RealOrComplex S> LogDeterminantResult<S> reduce_log_determinant(LuFactorization<S> const& factor, Precision p)
39{
40 auto phase = scalar_at_precision<S>(1, p);
41 auto sum = uni20::real(scalar_at_precision<S>(0, p));
42 auto correction = sum;
43 auto const& a = factor.packed();
44 for (std::size_t k = 0; k < factor.order(); ++k)
45 {
46 auto diagonal = a[k, k];
47 auto magnitude = lu_magnitude(diagonal);
48 auto term = math::log(magnitude.scale) + math::log(magnitude.unit);
49 // Neumaier reduction retains small contributions when logarithms cancel.
50 auto next = sum + term;
51 if (math::abs(sum) >= math::abs(term))
52 correction += (sum - next) + term;
53 else
54 correction += (term - next) + sum;
55 sum = std::move(next);
56 if constexpr (Complex<S>)
57 {
58 phase *= (diagonal / magnitude.scale) / magnitude.unit;
59 // Avoid accumulated drift of the product away from unit modulus.
60 auto phase_magnitude = lu_magnitude(phase);
61 phase = (phase / phase_magnitude.scale) / phase_magnitude.unit;
62 }
63 else if (diagonal < 0)
64 phase = -phase;
65 if (factor.pivots()[k] != k) phase = -phase;
66 if (!uni20::isfinite(sum) || !uni20::isfinite(correction) || !uni20::isfinite(phase))
67 return {.info = {.status = SolveStatus::nonfinite_result}, .value = {}};
68 }
69 sum += correction;
70 if (!uni20::isfinite(sum)) return {.info = {.status = SolveStatus::nonfinite_result}, .value = {}};
71 return {.info = {}, .value = LogDeterminant<S>{std::move(phase), std::move(sum)}};
72}
73} // namespace detail
74
78template <RealOrComplex S> [[nodiscard]] LogDeterminantResult<S> slogdet_with_info(LuFactorization<S> const& factor)
79{
80 return detail::reduce_log_determinant(factor, factor.precision());
81}
82
83namespace detail
84{
85template <KernelBackendSelector Backend, RankedTensorView<2> A>
86auto slogdet_matrix_impl(Backend&& backend, A const& a, SolveOptions<make_real_t<tensor_element_t<A>>> const& options,
88{
89 using S = tensor_element_t<A>;
90 auto attempt = lu_factor_impl(
91 std::forward<Backend>(backend), a,
92 LuOptions<make_real_t<S>>{.relative_pivot_tolerance = options.relative_pivot_tolerance, .retain_norms = false},
93 p);
94 if (attempt.factor) return slogdet_with_info(*attempt.factor);
95 LogDeterminantResult<S> result{.info = attempt.info, .value = {}};
96 if (attempt.info.status == SolveStatus::singular) result.value = zero_determinant<S>(p);
97 return result;
98}
99} // namespace detail
100
102template <KernelBackendSelector Backend, RankedTensorView<2> A>
103 requires RealOrComplex<tensor_element_t<A>>
104[[nodiscard]] auto slogdet_with_info(Backend&& backend, A const& a, Precision p,
105 SolveOptions<make_real_t<tensor_element_t<A>>> const& options = {})
106{
107 return detail::slogdet_matrix_impl(std::forward<Backend>(backend), a, options, p);
108}
109
114template <KernelBackendSelector Backend, RankedTensorView<2> A>
115 requires RealOrComplex<tensor_element_t<A>>
116[[nodiscard]] auto slogdet_with_info(Backend&& backend, A const& a,
117 SolveOptions<make_real_t<tensor_element_t<A>>> const& options = {})
118{
119 return detail::slogdet_matrix_impl(std::forward<Backend>(backend), a, options, precision_of(a));
120}
121
123template <RankedTensorView<2> A, class... Options>
124 requires RealOrComplex<tensor_element_t<A>>
125[[nodiscard]] auto slogdet_with_info(A const& a, Options const&... options)
126{
127 auto backend = select_backend_for<DenseMatrix<tensor_element_t<A>>>(lu_factor_op{});
128 return slogdet_with_info(backend, a, options...);
129}
130
133template <class... Args>
134 requires requires(Args&&... args) { slogdet_with_info(std::forward<Args>(args)...); }
135[[nodiscard]] auto slogdet(Args&&... args)
136{
137 auto result = slogdet_with_info(std::forward<Args>(args)...);
138 ERROR_IF(!result.value, "logarithmic determinant could not be resolved", static_cast<int>(result.info.status));
139 return std::move(*result.value);
140}
141} // namespace uni20::linalg
Exact arithmetic or an explicit binary working precision.
Definition core/precision.hpp:13
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
Concept for complex scalar types.
Definition scalar_concepts.hpp:75
typename make_real< T >::type make_real_t
Alias for the underlying real-valued type of T.
Definition scalar_traits.hpp:168
constexpr T & real(detail::standard_complex< T > &z) noexcept
Provides mutable access to the real component of a uni20::complex value.
Definition math.hpp:173
constexpr bool isfinite(I const &x) noexcept
Returns whether an integer scalar is finite.
Definition math.hpp:121
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
Definition linalg/async.hpp:28
LogDeterminantResult< S > slogdet_with_info(LuFactorization< S > const &factor)
Reduce completed factors in O(n), without refactoring or changing them.
Definition slogdet.hpp:78
@ singular
An exactly zero elimination pivot was found.
@ nonfinite_result
A factor, solution, reduction, or required magnitude became nonfinite.
auto slogdet(Args &&... args)
Return the log-determinant, including conventional zero for singular matrices.
Definition slogdet.hpp:135
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::log_fn log
Natural logarithm with scalar-specific precision handling.
Definition math.hpp:467
Definition data_table.cpp:8
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 Precision precision_of(T const &value)
Read scalar precision without changing any scalar or provider state.
Definition runtime_precision.hpp:53
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
Owning reusable host LU factors and solves through operation dispatch.
Logarithmic determinant with numerical diagnostics.
Definition slogdet.hpp:25
std::optional< LogDeterminant< S > > value
Definition slogdet.hpp:27
SolveInfo info
Definition slogdet.hpp:26
Determinant represented without multiplying diagonal magnitudes.
Definition slogdet.hpp:16
make_real_t< S > log_absolute
Definition slogdet.hpp:18
S phase
Definition slogdet.hpp:17
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
Fixed-output packed LU factorization with zero-based row-swap pivots.
Definition operation_tags.hpp:165
#define ERROR_IF(cond,...)
Definition trace.hpp:389