alpaqa 1.1.0a1
Nonconvex constrained optimization
Loading...
Searching...
No Matches
sparse-ops.hpp
Go to the documentation of this file.
1#pragma once
2
4#include <guanaqo/enumerate.hpp>
5#include <guanaqo/iter-adapter.hpp>
6#include <guanaqo/set-intersection.hpp>
7
8#include <algorithm>
9#include <cassert>
10#include <ranges>
11
12#include <Eigen/Sparse>
13
14namespace alpaqa::util {
15
16namespace detail {
17
18/// Returns a range over the row indices in the given column of @p sp_mat that
19/// are also in @p mask.
20/// The range consists of the full Eigen InnerIterators (row, column, value).
21template <class SpMat, class MaskVec>
22auto select_rows_in_col(const SpMat &sp_mat, MaskVec &mask, auto column) {
23 // Make a range that iterates over all matrix elements in the given column:
24 using row_iter_t = typename SpMat::InnerIterator;
25 guanaqo::iter_range_adapter<row_iter_t> col_range{{sp_mat, column}};
26 // Projector that extracts the row index from an element of that range:
27 static constexpr auto proj_row = [](const row_iter_t &it) {
28 return static_cast<typename MaskVec::value_type>(it.row());
29 };
30 // Compute the intersection between the matrix elements and the mask:
31 auto intersection = guanaqo::iter_set_intersection(
32 std::move(col_range), std::ranges::ref_view{mask}, std::less{},
33 proj_row);
34 // Extract just the iterator to the matrix element (dropping the mask):
35 auto extract_eigen_iter = []<class T>(T &&tup) -> decltype(auto) {
36 return std::get<0>(std::forward<T>(tup));
37 };
38 return std::views::transform(std::move(intersection), extract_eigen_iter);
39}
40
41/// Like @ref select_rows_in_col, but returns a range of tuples containing the
42/// Eigen InnerIterator and a linear index into the mask.
43template <class SpMat, class MaskVec>
44auto select_rows_in_col_iota(const SpMat &sp_mat, MaskVec &mask, auto column) {
45 // Make a range that iterates over all matrix elements in the given column:
46 using row_iter_t = typename SpMat::InnerIterator;
47 guanaqo::iter_range_adapter<row_iter_t> col_range{{sp_mat, column}};
48 // Projector that extracts the row index from an element of that range:
49 static constexpr auto proj_row = [](const row_iter_t &it) {
50 return static_cast<typename MaskVec::value_type>(it.row());
51 };
52 // Make a range of tuples of the index into the mask and the mask value:
53 auto iota_mask = guanaqo::enumerate(std::ranges::ref_view{mask});
54 // Projector that extracts the mask value from such a tuple:
55 static constexpr auto proj_mask = [](const auto &tup) -> decltype(auto) {
56 return std::get<1>(tup);
57 };
58 // Compute the intersection between the matrix elements and the mask:
59 auto intersection = guanaqo::iter_set_intersection(
60 std::move(col_range), std::move(iota_mask), std::less{}, proj_row,
61 proj_mask);
62 // Extract the iterator to the matrix element and the index into the mask:
63 auto extract_eigen_iter_and_index = []<class T>(T &&tup)
64 requires(std::is_rvalue_reference_v<T &&>)
65 {
66 auto &[eigen_iter, enum_tup] = tup;
67 auto &mask_index = std::get<0>(enum_tup);
68 return std::tuple{std::move(eigen_iter), std::move(mask_index)};
69 };
70 return std::views::transform(std::move(intersection),
71 extract_eigen_iter_and_index);
72}
73
74} // namespace detail
75
76/// R += R_full(mask,mask)
77template <class SpMat, class Mat, class MaskVec>
78void sparse_add_masked(const SpMat &R_full, Mat &&R, const MaskVec &mask) {
79 // Iterate over all columns in the mask
80 for (auto [ci, c] : guanaqo::enumerate(mask))
81 // Iterate over rows in intersection of mask and sparse column
82 for (auto [r, ri] : detail::select_rows_in_col_iota(R_full, mask, c))
83 R(ri, ci) += r.value();
84}
85
86/// S += S_full(mask,:)
87template <class SpMat, class Mat, class MaskVec>
88void sparse_add_masked_rows(const SpMat &S_full, Mat &&S, const MaskVec &mask) {
89 using index_t = typename SpMat::Index;
90 // Iterate over all columns
91 for (index_t c = 0; c < S_full.cols(); ++c)
92 // Iterate over rows in intersection of mask and sparse column
93 for (auto [r, ri] : detail::select_rows_in_col_iota(S_full, mask, c))
94 S(ri, c) += r.value();
95}
96
97/// out += R(mask_J,mask_K) * v(mask_K);
98template <class SpMat, class CVec, class Vec, class MaskVec>
99void sparse_matvec_add_masked_rows_cols(const SpMat &R, const CVec &v,
100 Vec &&out, const MaskVec &mask_J,
101 const MaskVec &mask_K) {
103 // Iterate over all columns in the mask K
104 for (auto c : mask_K)
105 // Iterate over rows in intersection of mask J and sparse column
106 for (auto &&[r, ri] : select_rows_in_col_iota(R, mask_J, c))
107 out(ri) += r.value() * v(c);
108}
109
110/// out += S(mask,:)ᵀ * v(mask);
111template <class SpMat, class CVec, class Vec, class MaskVec>
112void sparse_matvec_add_transpose_masked_rows(const SpMat &S, const CVec &v,
113 Vec &&out, const MaskVec &mask) {
114 using index_t = typename SpMat::Index;
115 // Iterate over all rows of Sᵀ
116 for (index_t c = 0; c < S.cols(); ++c)
117 // Iterate over columns in intersection of mask K and sparse row
118 for (auto r : detail::select_rows_in_col(S, mask, c))
119 out(c) += r.value() * v(r.row());
120}
121
122} // namespace alpaqa::util
auto select_rows_in_col(const SpMat &sp_mat, MaskVec &mask, auto column)
Returns a range over the row indices in the given column of sp_mat that are also in mask.
auto select_rows_in_col_iota(const SpMat &sp_mat, MaskVec &mask, auto column)
Like select_rows_in_col, but returns a range of tuples containing the Eigen InnerIterator and a linea...
void sparse_matvec_add_masked_rows_cols(const SpMat &R, const CVec &v, Vec &&out, const MaskVec &mask_J, const MaskVec &mask_K)
out += R(mask_J,mask_K) * v(mask_K);
void sparse_add_masked_rows(const SpMat &S_full, Mat &&S, const MaskVec &mask)
S += S_full(mask,:)
void sparse_add_masked(const SpMat &R_full, Mat &&R, const MaskVec &mask)
R += R_full(mask,mask)
void sparse_matvec_add_transpose_masked_rows(const SpMat &S, const CVec &v, Vec &&out, const MaskVec &mask)
out += S(mask,:)ᵀ * v(mask);
typename Conf::index_t index_t
Definition config.hpp:104