proxsuite 0.6.7
The Advanced Proximal Optimization Toolbox
Loading...
Searching...
No Matches
rowmod.hpp
Go to the documentation of this file.
1
2//
3// Copyright (c) 2022 INRIA
4//
5#ifndef PROXSUITE_LINALG_SPARSE_LDLT_ROWMOD_HPP
6#define PROXSUITE_LINALG_SPARSE_LDLT_ROWMOD_HPP
7
9#include <algorithm>
10
11namespace proxsuite {
12namespace linalg {
13namespace sparse {
14
23template<typename T, typename I>
24auto
37
50template<typename T, typename I>
51auto
53 I* etree,
54 I const* perm_inv,
55 isize pos,
56 DynStackMut stack) noexcept(false) -> MatMut<T, I>
57{
58 // step 1: delete row k from each column
59 VEG_ASSERT(!ld.is_compressed());
60
61 // we're actually deleting perm_inv[k], so that k is deleted in the permuted
62 // matrix
63 usize permuted_pos =
64 perm_inv == nullptr ? usize(pos) : util::zero_extend(perm_inv[pos]);
65
66 auto petree = etree;
67 I* pldi = ld.row_indices_mut();
68 T* pldx = ld.values_mut();
69 I* pldnz = ld.nnz_per_col_mut();
70
71 for (usize j = 0; j < permuted_pos; ++j) {
72 auto col_start = ld.col_start(j) + 1;
73 auto col_end = ld.col_end(j);
74 // search for the first row in column j greater than or equal to k
75 auto it =
76 std::lower_bound(pldi + col_start, pldi + col_end, I(permuted_pos));
77
78 // if an element was found, and it is equal to k
79 if ((it != (pldi + col_end)) && *it == I(permuted_pos)) {
80 usize it_pos = usize(it - (pldi + col_start));
81 usize count = (col_end - col_start - it_pos);
82 // shift all the row indices back by one position
83 // to delete row k
84 std::memmove(it, it + 1, count * sizeof(I));
85 T* itx = pldx + col_start + it_pos;
86
88 // shift all the values back by one position
89 std::memmove(itx, itx + 1, count * sizeof(T));
90
91 // decrement the non zero count
92 --pldnz[j];
93 ld._set_nnz(ld.nnz() - 1);
94
95 // adjust the parent of j in the elimination tree if necessary
96 if (petree[j] == I(permuted_pos)) {
97 VEG_ASSERT(it_pos == 0);
98 if (pldnz[j] > 1) {
99 petree[j] = *it;
100 } else {
101 petree[j] = I(-1);
102 }
103 }
104 }
105 }
106
107 // step 2: set d_kk = 1
108 T d_old = ld.values()[ld.col_start(permuted_pos)];
109 ld.values_mut()[ld.col_start(permuted_pos)] = 1;
110
111 // step 3: perform rank update
112 isize len = isize(util::zero_extend(ld.nnz_per_col()[permuted_pos])) - 1;
114 ld,
115 etree,
116 static_cast<I const*>(nullptr),
118 from_raw_parts,
119 ld.nrows(),
120 len,
121 pldi + ld.col_start(permuted_pos) + 1,
122 pldx + ld.col_start(permuted_pos) + 1,
123 },
124 d_old,
125 stack);
126 // step 4: delete col k_
127 ld.nnz_per_col_mut()[permuted_pos] = 1;
128 petree[permuted_pos] = I(-1);
129 return ld;
130}
141template<typename T, typename I>
142auto
146 isize n,
147 bool id_perm,
148 isize nnz,
150{
152 auto numerical_work = StackReq{ n * isize{ sizeof(T) }, isize{ alignof(T) } };
153 auto permuted_indices =
154 StackReq{ (id_perm ? 0 : nnz) * isize{ sizeof(I) }, isize{ alignof(I) } };
155 auto pattern_diff = StackReq{ n * isize{ sizeof(I) }, isize{ alignof(I) } };
156 auto merge =
160 n,
161 true,
162 max_nnz);
163
164 auto req = numerical_work;
166 req = req & pattern_diff;
167 req = req & merge;
168 req = req | update;
169
170 return req;
171}
188template<typename T, typename I>
189auto
191 I* etree,
192 I const* perm_inv,
193 isize pos,
196 DynStackMut stack) noexcept(false) -> MatMut<T, I>
197{
198 VEG_ASSERT(!ld.is_compressed());
199 bool id_perm = perm_inv == nullptr;
200 auto zx = util::zero_extend;
201
202 I* pldp = ld.col_ptrs_mut();
203 I* pldnz = ld.nnz_per_col_mut();
204 I* pldi = ld.row_indices_mut();
205 T* pldx = ld.values_mut();
206
207 // actually inserting in the position perm_inv[k] so that row k is added in
208 // the permuted matrix
209 usize permuted_pos = id_perm ? usize(pos) : zx(perm_inv[pos]);
211
212 {
213 // allocate workspace for numerical step, storage for the k-th row and k-th
214 // column of the new matrix
215 auto _lx2_storage = stack.make_new_for_overwrite(
216 proxsuite::linalg::veg::Tag<T>{}, ld.nrows());
217 auto plx2_storage = _lx2_storage.ptr_mut();
218
219 // allocate workspace for permuted row indices of the new column if
220 // necessary
221 auto _new_col_permuted_indices = stack.make_new_for_overwrite(
222 proxsuite::linalg::veg::Tag<I>{}, id_perm ? isize(0) : new_col.nnz());
223
225 id_perm ? new_col.row_indices() : _new_col_permuted_indices.ptr();
226
227 // copy and sort permuted row indices
228 if (!id_perm) {
230 for (usize k = 0; k < usize(new_col.nnz()); ++k) {
231 usize i = zx(new_col.row_indices()[k]);
232 pnew_col_permuted_indices[k] = perm_inv[i];
233 }
236 }
237
238 // allocate workspace for non-zero pattern of k-th row
239 auto _l12_nnz_pattern = stack.make_new_for_overwrite(
241 auto _difference = stack.make_new_for_overwrite(
242 proxsuite::linalg::veg::Tag<I>{}, ld.nrows() - isize(permuted_pos));
243 auto pdifference = _difference.ptr_mut();
244
245 auto pl12_nnz_pattern = _l12_nnz_pattern.ptr_mut();
246 usize l12_nnz_pattern_count = 0;
247
248 // the non-zero pattern is the set of columns reachable from the non-zero
249 // pattern of the added column through graph of L_{1..k,1..k}
250 // instead of graph traversal, we can use the k-th elimination subtree as we
251 // did in the initial factorization step
252
253 // for each row in the added column
254 {
255 auto _visited = stack.make_new(proxsuite::linalg::veg::Tag<bool>{},
256 isize(permuted_pos));
257 bool* visited = _visited.ptr_mut();
258 for (usize p = 0; p < usize(new_col.nnz()); ++p) {
259 auto j = zx(new_col_permuted_indices[p]);
260 if (j >= permuted_pos) {
261 break;
262 }
263
264 // add the ancestors of the corresponding column
265 // ancestors are not sorted, but they are added in topological order,
266 // which suffices for the triangular solve
267 while (true) {
268 if (visited[j]) {
269 break;
270 }
271 visited[j] = true;
274
275 j = util::sign_extend(etree[j]);
276 if (j == usize(-1) || j >= permuted_pos || visited[j]) {
277 break;
278 }
279 }
280 }
281 }
283
284 // zero the elements in the non-zero pattern of the solution (new k-th row)
285 for (usize p = 0; p < l12_nnz_pattern_count; ++p) {
287 }
288
289 // insert the rhs of the k-th row triangular system in the top part of the
290 // storage, and the bottom part of the added column in the bottom part of
291 // the storage
292 for (usize p = 0; p < usize(new_col.nnz()); ++p) {
293 auto j = zx(new_col.row_indices()[p]);
294 auto permuted_j = id_perm ? j : zx(perm_inv[j]);
295 plx2_storage[permuted_j] = new_col.values()[p];
296
297 // add the row indices of the bottom part of the added column, to the
298 // k-th column of L
299 if (permuted_j > permuted_pos) {
300 usize nz = zx(pldnz[permuted_pos]);
304 ld._set_nnz(ld.nnz() + 1);
305 }
306 }
307 // sort the added row indices
308 std::sort(pldi + zx(pldp[permuted_pos]) + 1,
310
311 // TODO: fuse loops?
312
313 for (usize p = 0; p < l12_nnz_pattern_count; ++p) {
314 usize j = zx(pl12_nnz_pattern[p]);
315 auto col_start = ld.col_start(j);
316 auto col_end = ld.col_end(j);
317
318 // update the pattern of the k-th column of L, with that of the bottom
319 // part of the j-th column of L, ignoring the elements less than or equal
320 // to k
321 VEG_BIND(auto,
325 static_cast<T*>(nullptr),
326 pldi + (zx(pldp[permuted_pos]) + 1),
327 isize(zx(pldp[permuted_pos + 1]) - zx(pldp[permuted_pos])) - 1,
328 pldnz[permuted_pos] - 1,
329 {
330 unsafe,
331 from_raw_parts,
332 pldi + (zx(pldp[j]) + 1),
333 isize(zx(pldnz[j])) - 1,
334 },
335 I(permuted_pos),
336 false,
337 stack));
338 (void)_;
339 (void)new_current_col;
340
341 // update column and global non-zero count
343 ld._set_nnz(ld.nnz() + computed_difference.len());
344
345 for (usize q = 0; q < usize(computed_difference.len()); ++q) {
347 }
348
349 // perform triangular solve and matrix vector product simultaneously
350 auto const xj = plx2_storage[j];
351 for (usize q = col_start + 1; q < col_end; ++q) {
352 auto i = zx(pldi[q]);
353 plx2_storage[i] -= pldx[q] * xj;
354 }
355 }
356
357 // insert the k-th row into L
358 for (usize p = 0; p < l12_nnz_pattern_count; ++p) {
359 // for each column in the non-zero pattern of the k-th row
360
361 usize j = zx(pl12_nnz_pattern[p]);
362 auto col_start = ld.col_start(j);
363 auto col_end = ld.col_end(j);
364 T d = pldx[col_start];
367
368 // check that we have enough space to insert one element
369 VEG_ASSERT(zx(pldnz[j]) < (zx(pldp[j + 1]) - zx(pldp[j])));
370
371 // find the first element greater than k
372 auto it =
373 std::lower_bound(pldi + col_start, pldi + col_end, I(permuted_pos));
374
375 // if it is the first element, update the elimination tree so that k is
376 // the new parent of column j
377 if (it == (pldi + col_start + 1)) {
378 etree[j] = I(permuted_pos);
379 }
380
381 // shift the row indices up by one position to provide enough space for
382 // the new element
383 std::memmove( //
384 it + 1,
385 it,
386 usize((pldi + col_end) - it) * sizeof(I));
387
389
390 // shift the values up by one position to provide enough space for the
391 // new element
392 std::memmove( //
393 pldx + (it - pldi) + 1,
394 pldx + (it - pldi),
395 usize((pldi + col_end) - it) * sizeof(T));
396
397 // insert the new row index k
398 *it = I(permuted_pos);
399 // insert the new corresponding value
400 *(pldx + (it - pldi)) = l12_elem / d;
401 // update the non-zero count
402 ++pldnz[j];
403 ld._set_nnz(ld.nnz() + 1);
404 }
405
406 // insert the k-th column of L
407 {
408 usize col_start = ld.col_start(permuted_pos);
409 usize col_end = ld.col_end(permuted_pos);
410 pldx[col_start] = diag_element;
411 for (usize p = col_start + 1; p < col_end; ++p) {
413 }
414 }
415 }
416
417 // set the parent of the k-th column of L
418 if (pldnz[permuted_pos] > 1) {
419 etree[permuted_pos] = pldi[ld.col_start(permuted_pos) + 1];
420 }
421
422 isize len = isize(util::zero_extend(ld.nnz_per_col()[permuted_pos])) - 1;
423 // perform the rank update with the newly added column
425 etree,
426 static_cast<I const*>(nullptr),
428 from_raw_parts,
429 ld.nrows(),
430 len,
431 pldi + ld.col_start(permuted_pos) + 1,
432 pldx + ld.col_start(permuted_pos) + 1,
433 },
435 stack);
436
437 return ld;
438}
439} // namespace sparse
440} // namespace linalg
441} // namespace proxsuite
442
443#endif /* end of include guard PROXSUITE_LINALG_SPARSE_LDLT_ROWMOD_HPP */
#define VEG_ASSERT(...)
#define VEG_CHECK_CONCEPT(...)
Definition macros.hpp:1241
auto delete_row_req(proxsuite::linalg::veg::Tag< T >, proxsuite::linalg::veg::Tag< I >, isize n, isize max_nnz) noexcept -> proxsuite::linalg::veg::dynstack::StackReq
Definition rowmod.hpp:25
auto delete_row(MatMut< T, I > ld, I *etree, I const *perm_inv, isize pos, DynStackMut stack) noexcept(false) -> MatMut< T, I >
Definition rowmod.hpp:52
auto merge_second_col_into_first(I *difference, T *first_values, I *first_ptr, PROXSUITE_MAYBE_UNUSED isize first_full_len, isize first_initial_len, Slice< I > second, proxsuite::linalg::veg::DoNotDeduce< I > ignore_threshold_inclusive, bool move_values, DynStackMut stack) noexcept(false) -> proxsuite::linalg::veg::Tuple< SliceMut< T >, SliceMut< I >, SliceMut< I > >
Definition update.hpp:33
VEG_INLINE void etree(I *parent, SymbolicMatRef< I > a, DynStackMut stack) noexcept
auto add_row(MatMut< T, I > ld, I *etree, I const *perm_inv, isize pos, VecRef< T, I > new_col, proxsuite::linalg::veg::DoNotDeduce< T > diag_element, DynStackMut stack) noexcept(false) -> MatMut< T, I >
Definition rowmod.hpp:190
auto rank1_update_req(proxsuite::linalg::veg::Tag< T >, proxsuite::linalg::veg::Tag< I >, isize n, bool id_perm, isize col_nnz) noexcept -> proxsuite::linalg::veg::dynstack::StackReq
Definition update.hpp:170
auto add_row_req(proxsuite::linalg::veg::Tag< T >, proxsuite::linalg::veg::Tag< I >, isize n, bool id_perm, isize nnz, isize max_nnz) noexcept -> proxsuite::linalg::veg::dynstack::StackReq
Definition rowmod.hpp:143
auto merge_second_col_into_first_req(proxsuite::linalg::veg::Tag< I >, isize second_size) noexcept -> proxsuite::linalg::veg::dynstack::StackReq
Definition update.hpp:21
#define VEG_BIND(CV_Auto, Identifiers, Tuple)
Definition tuple.hpp:53