guanaqo develop
Utilities for scientific software
Loading...
Searching...
No Matches
sparse-ops.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file
4/// @ingroup linalg_sparsity_utils
5/// Helpers for sparse matrix index ordering and conversions.
6
7#include <algorithm>
8#include <cassert>
9#include <numeric>
10#include <ranges>
11
12#if __cpp_lib_ranges_zip >= 202110L
13#define GUANAQO_SPARSE_SUPPORTS_SORTING 1
14#else
15#define GUANAQO_SPARSE_SUPPORTS_SORTING 0
16#endif
17
18namespace guanaqo::linalg {
19
20/// @ingroup linalg_sparsity_utils
21void convert_triplets_to_ccs(const auto &rows, const auto &cols,
22 auto &&inner_idx, auto &&outer_ptr,
23 auto idx_0 = 0) {
24 using Index = std::remove_cvref_t<decltype(*std::begin(inner_idx))>;
25 using IndexFrom = std::remove_cvref_t<decltype(*std::begin(cols))>;
26 using StorageIndex = std::remove_cvref_t<decltype(*std::begin(outer_ptr))>;
27 static_assert(std::is_same_v<decltype(idx_0), IndexFrom>);
28 // Inner indices: simply copy the row indices
29 assert(std::size(rows) == std::size(inner_idx));
30 auto cvt_rows = [&](auto r) {
31 return static_cast<Index>(r) - static_cast<Index>(idx_0);
32 };
33 std::ranges::ref_view rows_vw = rows;
34 std::ranges::transform(rows_vw, std::begin(inner_idx), cvt_rows);
35 // Outer indices: need to count the number of nonzeros per column
36 auto num_cols = static_cast<IndexFrom>(std::size(outer_ptr));
37 auto cols_iter = std::begin(cols);
38 for (IndexFrom c = 0; c < num_cols; ++c) {
39 cols_iter = std::lower_bound(cols_iter, std::end(cols), c + idx_0);
40 auto cz = static_cast<size_t>(c);
41 outer_ptr[cz] = static_cast<StorageIndex>(cols_iter - std::begin(cols));
42 }
43}
44
45#if GUANAQO_SPARSE_SUPPORTS_SORTING
46
47namespace detail {
48template <auto cmp, class... Ts>
49void sort_triplets_impl(Ts &&...triplets) {
50 auto indices = std::views::zip(std::ranges::ref_view{triplets}...);
51 std::ranges::sort(indices, cmp);
52}
53} // namespace detail
54
55/// Sort the (row, column, value) triplets, by column index first, then row.
56/// @ingroup linalg_sparsity_utils
57template <class... Ts>
58void sort_triplets(Ts &&...triplets) {
59 // Sort the indices (by column first, then row)
60 static constexpr auto cmp = [](const auto &a, const auto &b) {
61 return std::tie(std::get<1>(a), std::get<0>(a)) <
62 std::tie(std::get<1>(b), std::get<0>(b));
63 };
64 detail::sort_triplets_impl<cmp>(std::forward<Ts>(triplets)...);
65}
66
67/// Sort the (row, column, value) triplets by column index.
68/// @ingroup linalg_sparsity_utils
69template <class... Ts>
70void sort_triplets_col(Ts &&...triplets) {
71 // Sort the indices (by column)
72 static constexpr auto cmp = [](const auto &a, const auto &b) {
73 return std::get<1>(a) < std::get<1>(b);
74 };
75 detail::sort_triplets_impl<cmp>(std::forward<Ts>(triplets)...);
76}
77
78/// Given a sparse compressed-column storage matrix, sort all row indices
79/// within each column.
80/// @ingroup linalg_sparsity_utils
81template <class Outer, class... Inners>
82void sort_rows_csc(const Outer &outer_ptr, Inners &&...inners) {
83 if (outer_ptr.size() == 0)
84 return;
85 // Sort the indices (by row)
86 static constexpr auto cmp = [](const auto &a, const auto &b) {
87 return std::get<0>(a) < std::get<0>(b);
88 };
89 // TODO: could be parallelized
90 for (decltype(outer_ptr.size()) c = 0; c < outer_ptr.size() - 1; ++c) {
91 auto inner_start = outer_ptr[c];
92 auto inner_end = outer_ptr[c + 1];
93 auto indices = std::views::zip(
94 std::ranges::subrange{std::ranges::begin(inners) + inner_start,
95 std::ranges::begin(inners) + inner_end}...);
96 std::ranges::sort(indices, cmp);
97 }
98}
99
100/// Check that no two entries with the same row and column index exist in
101/// the given sparse coordinate list matrix. Assumes sorted indices.
102/// @ingroup linalg_sparsity_utils
103template <class... Ts>
104bool check_uniqueness_triplets(Ts &&...triplets) {
105 auto indices = std::views::zip(std::ranges::ref_view{triplets}...);
106 return std::ranges::adjacent_find(indices, std::equal_to<>{}) ==
107 std::ranges::end(indices);
108}
109
110#endif // GUANAQO_SPARSE_SUPPORTS_SORTING
111
112/// Check that no two entries with the same row and column index exist in
113/// the given sparse compressed-column storage matrix. Assumes sorted indices.
114/// @ingroup linalg_sparsity_utils
115template <class Outer, class Inner>
116bool check_uniqueness_csc(const Outer &outer_ptr, const Inner inner) {
117 if (outer_ptr.size() == 0)
118 return true;
119 auto is_unique_col = [&](auto &inner_start) {
120 auto inner_end = (&inner_start)[1];
121 auto indices =
122 std::ranges::subrange{std::ranges::begin(inner) + inner_start,
123 std::ranges::begin(inner) + inner_end};
124 return std::ranges::adjacent_find(indices, std::equal_to<>{}) ==
125 std::ranges::end(indices);
126 };
127 return std::transform_reduce(std::begin(outer_ptr), std::end(outer_ptr) - 1,
128 true, std::logical_and<>{}, is_unique_col);
129}
130
131} // namespace guanaqo::linalg
void sort_triplets(Ts &&...triplets)
Sort the (row, column, value) triplets, by column index first, then row.
bool check_uniqueness_csc(const Outer &outer_ptr, const Inner inner)
Check that no two entries with the same row and column index exist in the given sparse compressed-col...
void convert_triplets_to_ccs(const auto &rows, const auto &cols, auto &&inner_idx, auto &&outer_ptr, auto idx_0=0)
bool check_uniqueness_triplets(Ts &&...triplets)
Check that no two entries with the same row and column index exist in the given sparse coordinate lis...
void sort_rows_csc(const Outer &outer_ptr, Inners &&...inners)
Given a sparse compressed-column storage matrix, sort all row indices within each column.
void sort_triplets_col(Ts &&...triplets)
Sort the (row, column, value) triplets by column index.
void sort_triplets_impl(Ts &&...triplets)