akreon-linalg
Fixed-size linear algebra and numerical methods in C++26.
Loading...
Searching...
No Matches
ldl.hpp
Go to the documentation of this file.
1/*
2 * Copyright © 2025-2026 Akreon LLC
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6#pragma once
7
8#include <algorithm>
9
13
14namespace linalg {
15
21template <SquareMatrix Mat>
22struct LDL : SymmetricOperator<Mat, LDL<Mat>> {
28
31 using SymmetricBase::N;
32
33 static_assert(N > 1, "LDL^T requires at least a 2x2 matrix");
34
40 constexpr explicit LDL(const Mat& A) : SymmetricBase(A) { factor(A); }
41
48 constexpr explicit LDL(const Mat& A, const config::FactorConfig<T>& config_in)
50 factor(A);
51 }
52
58 constexpr auto D() const { return make_diag(D_vec); }
59
65 constexpr auto L() const { return make_unit_lower_triangle<Mat>(L_vec); }
66
75 constexpr auto solve(const Vector<T, N>& b) const {
76 const auto L_mat = L();
77 const auto y = forward_solve(L_mat, b);
78 const auto [z, event] = diag_solve(y);
79 if (is_fatal_event(event)) return std::tuple(z, event);
80
81 return std::tuple(back_solve(transpose(L_mat), z),
83 }
84
90 constexpr T det() const {
91 T res{1};
92 std::for_each(D_vec.begin(), D_vec.end(), [&](const auto& d) { res *= d; });
93 return res;
94 }
95
104 template <linalg::MatrixContainer MatIn>
105 requires(std::same_as<linalg::matrix_scalar_t<MatIn>, T>)
106 constexpr auto apply(const MatIn& Y) const {
107 constexpr std::size_t YRows = linalg::mat_rows_v<MatIn>;
108 constexpr std::size_t YCols = linalg::mat_cols_v<MatIn>;
109 static_assert(YRows == N, "Codom of Y must match expected dom of A");
110
112 for (std::size_t i = 0; i < YCols; ++i) {
113 const auto y_col = get_col(Y, i);
114 const auto [res, event] = solve(y_col);
115 if (linalg::is_fatal_event(event)) return std::tuple(Y, event);
116 for (std::size_t j = 0; j < YRows; ++j) A_inv_Y(j, i) = res(j);
117 }
118 return std::tuple(A_inv_Y, linalg::NumericalEvent::NONE);
119 }
120
121private:
127 constexpr void factor(Mat A) {
128 const auto& tol = config.tol;
129 auto ldl_t = [&]<std::size_t j = 0>(this auto&& self) -> bool {
130 // Advance one column at a time until L and D are complete.
131 if constexpr (j >= SymmetricBase::N) return true;
132 else {
133 // The first pivot has no contributions from earlier columns.
134 constexpr std::size_t next = j + 1;
135 if constexpr (j == 0) {
136 if (constexpr_abs(A(0, 0)) < tol) return false;
137
139 const auto col_up = (T(1) / A(0, 0)) * A_col;
140
142 } else {
143 // Form v_k = L(j,k)*D(k,k) and d_j = A(j,j) - L(j,:)*v.
144 Vector<T, j> v{};
145 for (std::size_t k = 0; k < j; ++k) v(k) = A(j, k) * A(k, k);
146
147 const auto A_row = get_block<j, next, 0, j>(A);
148 A(j, j) = A(j, j) - (A_row * v)(0);
149
150 if (constexpr_abs(A(j, j)) < tol) return false;
151
152 if constexpr (next < SymmetricBase::N) {
155
156 // Remove prior-column contributions, then divide by the new pivot.
157 const auto col_up =
158 T(1) / A(j, j) * (A_col - as_col_vector(A_blk * v));
159
161 }
162 }
163 return self.template operator()<next>();
164 }
165 };
166
167 if (!ldl_t()) {
169 return;
170 }
171
172 // Pack unit-lower L separately from diagonal D; D determines the inertia.
173 L_vec = get_lower_triangle(A);
174 D_vec = get_diag(A);
175
176 this->compute_definiteness(D_vec, config.tol);
177 }
178
186 constexpr auto forward_solve(const Mat& L_mat, const Vector<T, N>& b) const {
187 Vector<T, N> y{};
188 for (std::size_t i = 0; i < N; ++i) {
189 T sum(0);
190 for (std::size_t j = 0; j < i; ++j) sum += L_mat(i, j) * y(j);
191 y(i) = b(i) - sum;
192 }
193 return y;
194 }
195
202 constexpr auto diag_solve(const Vector<T, N>& y) const {
203 Vector<T, N> z{};
204
205 AK_ASSERT(this->is_valid() &&
206 "LDL::diag_solve requires a valid factorization (non-singular)");
207 if (!this->is_valid())
209
210 for (std::size_t i = 0; i < N; ++i) z(i) = y(i) / D_vec(i);
211
212 return std::tuple(z, linalg::NumericalEvent::NONE);
213 }
214
222 constexpr auto back_solve(const Mat& L_mat, const Vector<T, N>& z) const {
223 Vector<T, N> x{};
224 for (int i = static_cast<int>(N) - 1; i >= 0; --i) {
225 T sum(0);
226 for (int j = i + 1; j < static_cast<int>(N); ++j)
227 sum += L_mat(i, j) * x(j);
228 x(i) = z(i) - sum;
229 }
230 return x;
231 }
232
235 Vector<T, DIM - N> L_vec;
236 Vector<T, N> D_vec;
237};
238
239} // namespace linalg
#define AK_ASSERT(x)
Definition assert.hpp:13
constexpr auto get_diag(const Mat &A)
get the diagonal of a matrix A
constexpr auto get_lower_triangle(const Mat &A)
get lower triangle
constexpr auto make_diag(const Vector< T, N > &X)
make a diagonal matrix from a vector
Vector(T, Ts...) -> Vector< std::common_type_t< T, Ts... >, 1+sizeof...(Ts)>
Deduce coordinate type and dimension from constructor arguments.
constexpr auto transpose(const Vec &x)
tranpose of a vector
constexpr T constexpr_abs(T x)
Compute real absolute value or complex magnitude.
constexpr std::size_t mat_rows_v
Registered compile-time row count after cv/ref normalization.
constexpr bool is_fatal_event(NumericalEvent event)
is_fatal_event
constexpr auto get_col(const Mat &A, std::size_t idx)
get the column of matrix A based on given index
constexpr auto as_col_vector(const Vec &X)
express the vector X as a column vector (N x 1)
Configuration for generic numerical factorizations.
std::complex< ScalarT > ComplexT
matrix_scalar_t< Mat > T
alias helpers for types
static constexpr std::size_t N
helper to extract N
real_type_t< T > ScalarT
Factors a real symmetric matrix as A = L D L^T without pivoting.
Definition ldl.hpp:22
constexpr auto L() const
Reconstruct the unit lower-triangular factor.
Definition ldl.hpp:65
constexpr auto solve(const Vector< T, N > &b) const
Solve A x = b using the stored LDL^T factors.
Definition ldl.hpp:75
constexpr auto D() const
Reconstruct the diagonal factor.
Definition ldl.hpp:58
constexpr T det() const
Compute the determinant from the diagonal pivots.
Definition ldl.hpp:90
constexpr auto apply(const MatIn &Y) const
Solve A X = Y one column at a time.
Definition ldl.hpp:106
constexpr LDL(const Mat &A, const config::FactorConfig< T > &config_in)
Factor a symmetric matrix with explicit pivot tolerance.
Definition ldl.hpp:48
constexpr LDL(const Mat &A)
Factor a symmetric matrix as A = L D L^T.
Definition ldl.hpp:40
Owning, fixed-size, row-major matrix container over a scalar field.
Definition matrix.hpp:44
forward declaration for symmetric operator
static constexpr std::size_t DIM
helpers to get DIM from mat
SymmetricOperatorType type
member variables
constexpr void compute_definiteness(const Vector< DiagT, N > &diag, const ScalarT &tol)
compute definiteness
constexpr const LDL< Mat > & self() const
self
Owning, fixed-size vector container.
Definition vector.hpp:36