alpaqa 1.0.0a9
Nonconvex constrained optimization
Loading...
Searching...
No Matches
sparse-ops.hpp
Go to the documentation of this file.
1#pragma once
2
8
9#include <cassert>
10#include <chrono>
11#include <iostream>
12#include <ranges>
13#include <span>
14
15#include <Eigen/Sparse>
16
17namespace alpaqa::util {
18
19namespace detail {
20
21/// Returns a range over the row indices in the given column of @p sp_mat that
22/// are also in @p mask.
23/// The range consists of the full Eigen InnerIterators (row, column, value).
24template <class SpMat, class MaskVec>
25auto select_rows_in_col(const SpMat &sp_mat, MaskVec &mask, auto column) {
26 // Make a range that iterates over all matrix elements in the given column:
27 using row_iter_t = typename SpMat::InnerIterator;
28 util::iter_range_adapter<row_iter_t> col_range{{sp_mat, column}};
29 // Projector that extracts the row index from an element of that range:
30 static constexpr auto proj_row = [](const row_iter_t &it) {
31 return static_cast<typename MaskVec::value_type>(it.row());
32 };
33 // Compute the intersection between the matrix elements and the mask:
34 auto intersection = util::iter_set_intersection(std::move(col_range),
35 std::ranges::ref_view{mask},
36 std::less{}, proj_row);
37 // Extract just the iterator to the matrix element (dropping the mask):
38 auto extract_eigen_iter = []<class T>(T &&tup) -> decltype(auto) {
39 return std::get<0>(std::forward<T>(tup));
40 };
41 return std::views::transform(std::move(intersection), extract_eigen_iter);
42}
43
44/// Like @ref select_rows_in_col, but returns a range of tuples containing the
45/// Eigen InnerIterator and a linear index into the mask.
46template <class SpMat, class MaskVec>
47auto select_rows_in_col_iota(const SpMat &sp_mat, MaskVec &mask, auto column) {
48 // Make a range that iterates over all matrix elements in the given column:
49 using row_iter_t = typename SpMat::InnerIterator;
50 util::iter_range_adapter<row_iter_t> col_range{{sp_mat, column}};
51 // Projector that extracts the row index from an element of that range:
52 static constexpr auto proj_row = [](const row_iter_t &it) {
53 return static_cast<typename MaskVec::value_type>(it.row());
54 };
55 // Make a range of tuples of the index into the mask and the mask value:
56 auto iota_mask = util::enumerate(std::ranges::ref_view{mask});
57 // Projector that extracts the mask value from such a tuple:
58 static constexpr auto proj_mask = [](const auto &tup) -> decltype(auto) {
59 return std::get<1>(tup);
60 };
61 // Compute the intersection between the matrix elements and the mask:
62 auto intersection =
63 util::iter_set_intersection(std::move(col_range), std::move(iota_mask),
64 std::less{}, proj_row, proj_mask);
65 // Extract the iterator to the matrix element and the index into the mask:
66 auto extract_eigen_iter_and_index = []<class T>(T && tup)
67 requires(std::is_rvalue_reference_v<T &&>)
68 {
69 auto &[eigen_iter, enum_tup] = tup;
70 auto &mask_index = std::get<0>(enum_tup);
71 return std::tuple{std::move(eigen_iter), std::move(mask_index)};
72 };
73 return std::views::transform(std::move(intersection),
74 extract_eigen_iter_and_index);
75}
76
77} // namespace detail
78
79/// R += R_full(mask,mask)
80template <class SpMat, class Mat, class MaskVec>
81void sparse_add_masked(const SpMat &R_full, Mat &&R, const MaskVec &mask) {
82 // Iterate over all columns in the mask
83 for (auto [ci, c] : util::enumerate(mask))
84 // Iterate over rows in intersection of mask and sparse column
85 for (auto [r, ri] : detail::select_rows_in_col_iota(R_full, mask, c))
86 R(ri, ci) += r.value();
87}
88
89/// S += S_full(mask,:)
90template <class SpMat, class Mat, class MaskVec>
91void sparse_add_masked_rows(const SpMat &S_full, Mat &&S, const MaskVec &mask) {
92 using index_t = typename SpMat::Index;
93 // Iterate over all columns
94 for (index_t c = 0; c < S_full.cols(); ++c)
95 // Iterate over rows in intersection of mask and sparse column
96 for (auto [r, ri] : detail::select_rows_in_col_iota(S_full, mask, c))
97 S(ri, c) += r.value();
98}
99
100/// out += R(mask_J,mask_K) * v(mask_K);
101template <class SpMat, class CVec, class Vec, class MaskVec>
102void sparse_matvec_add_masked_rows_cols(const SpMat &R, const CVec &v,
103 Vec &&out, const MaskVec &mask_J,
104 const MaskVec &mask_K) {
106 // Iterate over all columns in the mask K
107 for (auto c : mask_K)
108 // Iterate over rows in intersection of mask J and sparse column
109 for (auto &&[r, ri] : select_rows_in_col_iota(R, mask_J, c))
110 out(ri) += r.value() * v(c);
111}
112
113/// out += S(mask,:)ᵀ * v(mask);
114template <class SpMat, class CVec, class Vec, class MaskVec>
115void sparse_matvec_add_transpose_masked_rows(const SpMat &S, const CVec &v,
116 Vec &&out, const MaskVec &mask) {
117 using index_t = typename SpMat::Index;
118 // Iterate over all rows of Sᵀ
119 for (index_t c = 0; c < S.cols(); ++c)
120 // Iterate over columns in intersection of mask K and sparse row
121 for (auto r : detail::select_rows_in_col(S, mask, c))
122 out(c) += r.value() * v(r.row());
123}
124
125#if __cpp_lib_ranges_zip >= 202110L && __cpp_lib_ranges_enumerate >= 202302L
126
127template <Config Conf>
128void convert_triplets_to_ccs(const auto &rows, const auto &cols,
129 rindexvec<Conf> inner_idx,
130 rindexvec<Conf> outer_ptr,
131 index_t<Conf> idx_0 = 0) {
133 // Inner indices: simply the row indices
134 assert(std::size(rows) == std::size(inner_idx));
135 auto cvt_indices = [&](auto i) { return static_cast<index_t>(i) - idx_0; };
136 std::ranges::ref_view rows_vw = rows;
137 std::ranges::transform(rows_vw, std::begin(inner_idx), cvt_indices);
138 // Outer indices: need to count the number of nonzeros per column
139 auto cols_iter = std::begin(cols);
140 for (auto &&[i, outer] : std::views::enumerate(outer_ptr)) {
141 cols_iter = std::lower_bound(cols_iter, std::end(cols), i + idx_0);
142 outer = static_cast<index_t>(cols_iter - std::begin(cols));
143 }
144}
145
146/// Sort the (row, column, value) triplets, column first, then row.
147template <class... Ts>
148void sort_triplets(Ts &&...triplets) {
149 // Sort the indices (column first, then row)
150 auto cmp = [](const auto &a, const auto &b) {
151 return std::tie(std::get<1>(a), std::get<0>(a)) <
152 std::tie(std::get<1>(b), std::get<0>(b));
153 };
154 auto indices = std::views::zip(std::ranges::ref_view{triplets}...);
155 auto t0 = std::chrono::steady_clock::now();
156 std::ranges::sort(indices, cmp);
157 auto t1 = std::chrono::steady_clock::now();
158 std::cout << "Sorting took: "
159 << std::chrono::duration<double>{t1 - t0}.count() * 1e6
160 << " µs\n";
161}
162
163#endif
164
165} // namespace alpaqa::util
#define USING_ALPAQA_CONFIG(Conf)
Definition: config.hpp:54
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.
Definition: sparse-ops.hpp:25
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...
Definition: sparse-ops.hpp:47
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);
Definition: sparse-ops.hpp:102
void sparse_add_masked_rows(const SpMat &S_full, Mat &&S, const MaskVec &mask)
S += S_full(mask,:)
Definition: sparse-ops.hpp:91
auto enumerate(Rng &&rng)
Definition: enumerate.hpp:66
void sparse_add_masked(const SpMat &R_full, Mat &&R, const MaskVec &mask)
R += R_full(mask,mask)
Definition: sparse-ops.hpp:81
void sparse_matvec_add_transpose_masked_rows(const SpMat &S, const CVec &v, Vec &&out, const MaskVec &mask)
out += S(mask,:)ᵀ * v(mask);
Definition: sparse-ops.hpp:115
set_intersection_iterable< std::ranges::views::all_t< R1 >, std::ranges::views::all_t< R2 >, Comp, Proj1, Proj2 > iter_set_intersection(R1 &&r1, R2 &&r2, Comp comp={}, Proj1 proj1={}, Proj2 proj2={})
typename Conf::rindexvec rindexvec
Definition: config.hpp:77
typename Conf::index_t index_t
Definition: config.hpp:75