proxsuite-nlp  0.11.0
A primal-dual augmented Lagrangian-type solver for nonlinear programming on manifolds.
 
Loading...
Searching...
No Matches
block-ldlt.hpp
Go to the documentation of this file.
1
6#pragma once
7
10
12
13#include <numeric>
14
15namespace proxsuite {
16namespace nlp {
17namespace linalg {
18
19namespace backend {
20
22template <typename Scalar> struct block_impl {
24 MatrixRef mat;
25 SymbolicBlockMatrix sym_structure;
27 bool ldlt_in_place_impl(SignMatrix &sign) {
28 if (!sym_structure.performed_llt) {
29 assert(false && "Block structure was not analyzed yet.");
30 return false;
31 }
32 const isize nblocks = sym_structure.nsegments();
33 const isize n = mat.rows();
34 if ((nblocks == 0) || (n <= 1)) {
35 return true;
36 }
37
38 const isize bs = sym_structure.segment_lens[0];
39 const isize rem = n - bs;
40 MatrixRef l00 = mat.block(0, 0, bs, bs);
41 MatrixRef l11 = mat.block(bs, bs, rem, rem);
42 const auto d0 = l00.diagonal();
43 const auto d0_inv = d0.asDiagonal().inverse();
44
45 MatrixRef work_tr = mat.block(0, bs, bs, rem);
46 Eigen::Transpose<MatrixRef> work = work_tr.transpose();
47
48 switch (sym_structure(0, 0)) {
49 case Zero:
50 case TriU:
51 case Dense:
52 return false;
53
54 case TriL: {
55 // compute l00
57
58 isize offset = bs;
59
60 for (isize i = 1; i < nblocks; ++i) {
61 const isize bsi = sym_structure.segment_lens[i];
62 MatrixRef li0 = mat.block(offset, 0, bsi, bs);
63 Eigen::Block<decltype(work)> li0_copy =
64 work.block(offset - bs, 0, bsi, bs);
65
66 switch (sym_structure(i, 0)) {
67 case TriL:
68 return false;
69 case TriU: {
70 auto li0_u = li0.template triangularView<Eigen::Upper>();
71 // PERF: replace li0.T by li0_tl
72 // auto li0_tl = li0.transpose().template
73 // triangularView<Eigen::Lower>();
74 l00.template triangularView<Eigen::UnitLower>().solveInPlace(
75 li0.transpose());
76 li0_copy.template triangularView<Eigen::Upper>() = li0_u;
77 li0_u = li0 * d0_inv;
78 break;
79 }
81 case Diag:
82 case Dense: {
83 l00.template triangularView<Eigen::UnitLower>().solveInPlace(
84 li0.transpose());
85 li0_copy = li0;
86 li0 = li0 * d0_inv;
87 break;
88 }
89 case Zero:
90 break;
91 }
92 offset += bsi;
93 }
94
95 break;
96 }
97 case Diag: {
98 // l00 is unchanged
99 for (isize k = 0; k < bs; k++) {
100 update_sign_matrix(sign, l00(k, k));
101 }
102 isize offset = bs;
103
104 for (isize i = 1; i < nblocks; ++i) {
105 const isize bsi = sym_structure.segment_lens[i];
106 MatrixRef li0 = mat.block(offset, 0, bsi, bs);
107 Eigen::Block<decltype(work)> li0_copy =
108 work.block(offset - bs, 0, bsi, bs);
109
110 switch (sym_structure(i, 0)) {
111 case TriL:
112 return false;
113 case Diag: {
114 auto li0_d = li0.diagonal();
115 li0_copy.diagonal() = li0_d;
116 li0_d = li0_d.cwiseQuotient(d0);
117 break;
118 }
119 case TriU: {
120 auto li0_u = li0.template triangularView<Eigen::Upper>();
121 li0_copy.template triangularView<Eigen::Upper>() = li0_u;
122 li0_u = li0 * d0_inv;
123 break;
124 }
125 case Dense: {
126 li0_copy = li0;
127 li0 = li0 * d0_inv;
128 break;
129 }
130 case Zero:
131 break;
132 }
133 offset += bsi;
134 }
135
136 break;
137 }
138 }
139
140 isize offset_i = bs;
141 for (isize i = 1; i < nblocks; ++i) {
142 const isize bsi = sym_structure.segment_lens[i];
143 Eigen::Block<MatrixRef> li0 = mat.block(offset_i, 0, bsi, bs);
144 Eigen::Block<decltype(work)> li0_prev =
145 work.block(offset_i - bs, 0, bsi, bs);
146
148 Eigen::Block<MatrixRef> target_ii =
149 mat.block(offset_i, offset_i, bsi, bsi);
150
151 // target_ii -= li0 * li0_prev.T;
153 backend::gemmt(target_ii, li0, li0_prev, sym_structure(i, 0),
154 sym_structure(i, 0), Scalar(-1));
155
156 isize offset_j = offset_i + bsi;
157 for (isize j = i + 1; j < nblocks; ++j) {
158 // target_ji -= lj0 * li0_prev.T;
159
160 const isize bsj = sym_structure.segment_lens[j];
161 Eigen::Block<MatrixRef> lj0 = mat.block(offset_j, 0, bsj, bs);
162 Eigen::Block<MatrixRef> target_ji =
163 mat.block(offset_j, offset_i, bsj, bsi);
164
165 backend::gemmt(target_ji, lj0, li0_prev, sym_structure(j, 0),
166 sym_structure(i, 0), Scalar(-1));
167
168 offset_j += bsj;
169 }
170
171 offset_i += bsi;
172 }
173
174 return block_impl{
175 l11,
176 sym_structure.submatrix(1, nblocks - 1),
177 }
178 .ldlt_in_place_impl(sign);
179 }
180};
181
182} // namespace backend
183
197template <typename _Scalar> struct BlockLDLT : ldlt_base<_Scalar> {
198 using Scalar = _Scalar;
200 using Base = ldlt_base<Scalar>;
201 using DView = typename Base::DView;
202 using Traits = LDLT_Traits<MatrixXs, Eigen::Lower>;
203 using PermutationType =
204 Eigen::PermutationMatrix<Eigen::Dynamic, Eigen::Dynamic, isize>;
205 using PermIdxType = Eigen::Matrix<isize, Eigen::Dynamic, 1>;
207 using BlockTriU =
208 TriangularBlockMatrix<const typename MatrixXs::AdjointReturnType,
209 Eigen::UnitUpper>;
210
211protected:
212 MatrixXs m_matrix;
213 SymbolicBlockMatrix m_structure;
214 SymbolicBlockMatrix m_struct_tr;
215 PermutationType m_permutation;
216 using Base::m_info;
217 using Base::m_sign;
218 std::vector<isize> m_perm;
219 std::vector<isize> m_perm_inv;
220 std::vector<isize> m_iwork;
221 std::vector<isize> m_start_idx;
222
223 BlockLDLT &updateBlockPermutationMatrix(SymbolicBlockMatrix const &in);
224
225public:
229 : Base(), m_matrix(size, size), m_structure(structure.copy()),
230 m_permutation(size), m_perm(nblocks()), m_perm_inv(nblocks()),
231 m_iwork(nblocks()), m_start_idx(nblocks()) {
232 std::iota(m_perm.begin(), m_perm.end(), isize(0));
233 m_permutation.setIdentity();
234 m_struct_tr = m_structure.transpose();
235 }
236
237 BlockLDLT(BlockLDLT const &other)
238 : BlockLDLT(other.m_matrix.rows(), other.m_structure) {
239 m_matrix = other.m_matrix;
240 m_permutation = other.m_permutation;
241 m_info = other.m_info;
242 m_perm = other.m_perm;
243 m_perm_inv = other.m_perm_inv;
244 m_iwork = other.m_iwork;
245 m_start_idx = other.m_start_idx;
246 }
247
250 m_start_idx[0] = 0;
251 for (usize i = 0; i < nblocks() - 1; ++i) {
252 m_start_idx[i + 1] = m_start_idx[i] + in.segment_lens[i];
253 }
254 }
255
256 ~BlockLDLT() {
257 delete[] m_structure.m_data;
258 delete[] m_structure.segment_lens;
259 delete[] m_struct_tr.m_data;
260 delete[] m_struct_tr.segment_lens;
261 m_structure.m_data = nullptr;
262 m_structure.segment_lens = nullptr;
263 }
264
266 inline const SymbolicBlockMatrix &structure() const { return m_structure; }
267 inline void print_sparsity() const { print_sparsity_pattern(m_structure); }
268
270 inline bool analyzePattern();
271
272 usize nblocks() const { return usize(m_structure.segments_count); }
273
275 void setBlockPermutation(isize const *new_perm = nullptr);
276
277 auto blockPermIndices() -> std::vector<isize> & { return m_perm; }
278
282
283 inline const PermutationType &permutationP() const { return m_permutation; }
284
285 MatrixXs reconstructedMatrix() const override;
286
287 inline typename Traits::MatrixL matrixL() const {
288 return Traits::getL(m_matrix);
289 }
290
291 inline typename Traits::MatrixU matrixU() const {
292 return Traits::getU(m_matrix);
293 }
294
295 inline DView vectorD() const override {
296 return Base::diag_view_impl(m_matrix);
297 }
298
300 template <typename Derived>
301 bool solveInPlace(Eigen::MatrixBase<Derived> &b) const;
302
303 template <typename Rhs>
304 typename Rhs::PlainObject solve(const Eigen::MatrixBase<Rhs> &rhs) const {
305 typename Rhs::PlainObject out = rhs;
306 solveInPlace(out);
307 return out;
308 }
309
310 const MatrixXs &matrixLDLT() const override { return m_matrix; }
311
312 inline void compute() {
313 m_info =
314 backend::block_impl<Scalar>{m_matrix, m_structure}.ldlt_in_place_impl(
315 m_sign)
316 ? Eigen::Success
317 : Eigen::NumericalIssue;
318 }
319
322 BlockLDLT &compute(const ConstMatrixRef &mat) override {
323 assert(mat.rows() == mat.cols());
324 m_matrix.conservativeResizeLike(mat);
325 auto mat_coeff = [&](isize i, isize j) {
326 return i >= j ? mat(i, j) : mat(j, i);
327 };
328 auto n = mat.rows();
329 auto indices = permutationP().indices();
330 // by column
331 for (isize j = 0; j < n; ++j) {
332 auto pj = indices[j];
333 // by line starting at j
334 for (isize i = j; i < n; ++i) {
335 m_matrix(i, j) = mat_coeff(indices[i], pj);
336 }
337 }
338 // m_matrix.noalias() = permutationP() * m_matrix;
339 // m_matrix.noalias() = m_matrix * permutationP().transpose();
340 // do not re-run analysis
341 compute();
342
343 return *this;
344 }
345};
346
347} // namespace linalg
348} // namespace nlp
349} // namespace proxsuite
350
351#include "./block-ldlt.hxx"
352
353#ifdef PROXSUITE_NLP_ENABLE_TEMPLATE_INSTANTIATION
354#include "./block-ldlt.txx"
355#endif
Routines for Cholesky factorization.
bool dense_ldlt_in_place(Eigen::MatrixBase< Derived > &a, SignMatrix &sign)
Definition dense.hpp:94
#define PROXSUITE_NLP_DYNAMIC_TYPEDEFS(Scalar)
Definition math.hpp:26
Specific linear algebra routines.
@ Dense
There is no known prior structure; assume a dense block.
@ Diag
The block is diagonal.
@ Zero
All entries in the block are zero.
@ TriL
The block is lower-triangular.
@ TriU
The block is upper-triangular.
Main package namespace.
Definition bcl-params.hpp:5
Block sparsity-aware LDLT factorization algorithm.
BlockLDLT & compute(const ConstMatrixRef &mat) override
BlockLDLT(isize size, SymbolicBlockMatrix const &structure)
The constructor copies the input matrix.
const SymbolicBlockMatrix & structure() const
bool solveInPlace(Eigen::MatrixBase< Derived > &b) const
Solve for the right-hand side in-place.
void computeStartIndices(const SymbolicBlockMatrix &in)
Compute indices indicating where blocks start.
void setBlockPermutation(isize const *new_perm=nullptr)
Calls updateBlockPermutationMatrix.
bool analyzePattern()
Analyze and factorize the block structure, if not done already.
BlockLDLT & findSparsifyingPermutation()
Find a sparsity-maximizing permutation of the blocks. This will also compute the symbolic factorizati...
Symbolic representation of the sparsity structure of a (square) block matrix.
Representation for triangular block matrices.
Implementation struct for the recursive block LDLT algorithm.
Base interface for LDLT solvers.
Definition ldlt-base.hpp:24