1#ifndef PBAT_MATH_LINALG_SPARSITYPATTERN_H
2#define PBAT_MATH_LINALG_SPARSITYPATTERN_H
4#include "PhysicsBasedAnimationToolkitExport.h"
11#include <range/v3/action/sort.hpp>
12#include <range/v3/range/conversion.hpp>
13#include <range/v3/view/iota.hpp>
14#include <range/v3/view/unique.hpp>
30template <
typename TIndex = Index, Eigen::StorageOptions Options = Eigen::ColMajor>
36 Eigen::SparseMatrix<std::uint8_t, Options, TIndex>;
38 PBAT_API SparsityPattern() =
default;
46 TRowIndexRange&& rowIndices,
47 TColIndexRange&& colIndices);
55 TRowIndexRange&& rowIndices,
56 TColIndexRange&& colIndices);
58 template <common::CArithmeticRange TNonZeroRange>
59 auto ToMatrix(TNonZeroRange&& nonZeros)
const
60 -> Eigen::SparseMatrix<std::ranges::range_value_t<TNonZeroRange>, Options, TIndex>;
62 template <common::CArithmeticRange TNonZeroRange,
class TDerived>
63 void To(TNonZeroRange&& nonZeros, Eigen::SparseCompressedBase<TDerived>& Ain)
const;
65 template <common::CArithmeticRange TNonZeroRange,
class TDerived>
66 void AddTo(TNonZeroRange&& nonZeros, Eigen::SparseCompressedBase<TDerived>& Ain)
const;
68 PBAT_API
bool IsEmpty()
const {
return mNonZeroIndexToValueIndex.empty(); }
70 template <common::CFloatingPo
int TScalar>
71 auto Pattern() const -> Eigen::SparseMatrix<TScalar, Options, TIndex>;
75 mNonZeroIndexToValueIndex;
79template <typename TIndex, Eigen::StorageOptions Options>
81 common::CContiguousIndexRange TRowIndexRange,
82 common::CContiguousIndexRange TColIndexRange>
86 TRowIndexRange&& rowIndices,
87 TColIndexRange&& colIndices)
92 std::forward<TRowIndexRange>(rowIndices),
93 std::forward<TColIndexRange>(colIndices));
96template <
typename TIndex, Eigen::StorageOptions Options>
100inline void SparsityPattern<TIndex, Options>::Compute(
103 TRowIndexRange&& rowIndices,
104 TColIndexRange&& colIndices)
106 PBAT_PROFILE_NAMED_SCOPE(
"pbat.math.linalg.SparsityPattern.Compute");
107 namespace srng = std::ranges;
108 namespace sviews = std::views;
109 namespace rng = ranges;
110 namespace views = rng::views;
112 auto const nRowIndices = srng::size(rowIndices);
113 auto const nColIndices = srng::size(colIndices);
114 if (nRowIndices != nColIndices)
116 std::string
const what = fmt::format(
117 "Expected same number of row indices and column indices, but got {} row indices "
118 "and {} column indices",
121 throw std::invalid_argument(what);
123 auto const [rowMin, rowMax] = srng::minmax_element(rowIndices);
124 auto const [colMin, colMax] = srng::minmax_element(colIndices);
125 bool const bRowsInBounds = (*rowMin >= 0) && (*rowMax < nRows);
126 bool const bColsInBounds = (*colMin >= 0) && (*colMax < nCols);
127 if (not(bRowsInBounds and bColsInBounds))
129 std::string
const what = fmt::format(
130 "Out of bounds min (row,col)=({},{}) and max (row,col)=({},{}), while "
131 "(nRows,nCols)=({},{})",
138 throw std::invalid_argument(what);
141 auto const nnz = nRowIndices;
142 mNonZeroIndexToValueIndex.resize(nnz,
IndexType{0});
144 auto rows = srng::data(rowIndices);
145 auto cols = srng::data(colIndices);
147 bool constexpr bIsRowMajor = (Options == Eigen::RowMajor);
148 auto const indexPair = [&](
Index s) {
149 if constexpr (bIsRowMajor)
150 return std::make_pair(rows[s], cols[s]);
152 return std::make_pair(cols[s], rows[s]);
155 return indexPair(lhs) < indexPair(rhs);
158 return indexPair(lhs) == indexPair(rhs);
160 auto const numNonZeroIndices =
static_cast<IndexType>(nnz);
161 auto const sortedNonZeroIndices = views::iota(
IndexType{0}, numNonZeroIndices) |
162 rng::to<std::vector>() | rng::actions::sort(less);
163 auto const sortedUniqueNonZeroIndices =
164 sortedNonZeroIndices | views::unique(equal) | rng::to<std::vector>();
166 Eigen::Vector<IndexType, Eigen::Dynamic> innerSizes;
167 innerSizes.setZero(bIsRowMajor ? nRows : nCols);
168 if constexpr (bIsRowMajor)
170 for (
auto u : sortedUniqueNonZeroIndices)
171 ++innerSizes[rows[u]];
175 for (
auto u : sortedUniqueNonZeroIndices)
176 ++innerSizes[cols[u]];
179 for (
auto k = 0, u = 0; k < numNonZeroIndices; ++k)
181 auto const s = sortedNonZeroIndices[
static_cast<std::size_t
>(k)];
182 auto const uu = sortedUniqueNonZeroIndices[
static_cast<std::size_t
>(u)];
183 bool const bIsSameEntry = equal(s, uu);
184 mNonZeroIndexToValueIndex[
static_cast<std::size_t
>(s)] =
185 static_cast<IndexType>(bIsSameEntry ? u : ++u);
187 mInnerPattern.resize(nRows, nCols);
188 mInnerPattern.reserve(innerSizes);
189 for (
auto s : sortedUniqueNonZeroIndices)
190 mInnerPattern.insert(rows[s], cols[s]) = 0;
191 mInnerPattern.makeCompressed();
194template <
typename TIndex, Eigen::StorageOptions Options>
195template <common::CArithmeticRange TNonZeroRange>
196inline auto SparsityPattern<TIndex, Options>::ToMatrix(TNonZeroRange&& nonZeros)
const
197 -> Eigen::SparseMatrix<std::ranges::range_value_t<TNonZeroRange>, Options, TIndex>
199 PBAT_PROFILE_NAMED_SCOPE(
"pbat.math.linalg.SparsityPattern.ToMatrix");
200 using ScalarType = std::ranges::range_value_t<TNonZeroRange>;
201 auto Acpy = Pattern<ScalarType>();
202 AddTo(std::forward<TNonZeroRange>(nonZeros), Acpy);
206template <
typename TIndex, Eigen::StorageOptions Options>
207template <common::CArithmeticRange TNonZeroRange,
class TDerived>
208inline void SparsityPattern<TIndex, Options>::AddTo(
209 TNonZeroRange&& nonZeros,
210 Eigen::SparseCompressedBase<TDerived>& Ain)
const
212 PBAT_PROFILE_NAMED_SCOPE(
"pbat.math.linalg.SparsityPattern.AddTo");
214 std::is_same_v<Scalar, std::ranges::range_value_t<TNonZeroRange>>,
215 "Only Scalar non-zero values are accepted");
217 namespace rng = std::ranges;
218 auto const nnz = rng::size(nonZeros);
219 if (nnz != mNonZeroIndexToValueIndex.size())
221 std::string
const what =
222 fmt::format(
"Expected {} non zeros, got {}", mNonZeroIndexToValueIndex.size(), nnz);
223 throw std::invalid_argument(what);
226 Scalar* values = Ain.valuePtr();
227 for (
auto k = 0ULL; k < nnz; ++k)
228 values[mNonZeroIndexToValueIndex[k]] += nonZeros[k];
231template <
typename TIndex, Eigen::StorageOptions Options>
232template <common::CArithmeticRange TNonZeroRange,
class TDerived>
233inline void SparsityPattern<TIndex, Options>::To(
234 TNonZeroRange&& nonZeros,
235 Eigen::SparseCompressedBase<TDerived>& Ain)
const
237 Ain.coeffs().setZero();
238 AddTo(std::forward<TNonZeroRange>(nonZeros), Ain);
241template <
typename TIndex, Eigen::StorageOptions Options>
242template <common::CFloatingPo
int TScalar>
243inline auto SparsityPattern<TIndex, Options>::Pattern() const
244 -> Eigen::SparseMatrix<TScalar, Options, TIndex>
246 return mInnerPattern.template cast<TScalar>();
Sparsity pattern precomputer to accelerate sparse matrix assembly.
Definition SparsityPattern.h:32
TIndex IndexType
Index type for the matrix indices.
Definition SparsityPattern.h:34
Eigen::SparseMatrix< std::uint8_t, Options, TIndex > InnerPatternType
Type of the inner pattern.
Definition SparsityPattern.h:35
Concepts for common types.
Contiguous range of integer types.
Definition Concepts.h:73
Common functionality.
Definition ArgSort.h:20
Linear Algebra related functionality.
Definition FilterEigenvalues.h:7
Math related functionality.
Definition Concepts.h:19
The main namespace of the library.
Definition Aliases.h:15
std::ptrdiff_t Index
Index type.
Definition Aliases.h:17
double Scalar
Scalar type.
Definition Aliases.h:18
Profiling utilities for the Physics-Based Animation Toolkit (PBAT)