SPH
SparseMatrix.cpp
Go to the documentation of this file.
1 #include "math/SparseMatrix.h"
2 
4 #ifdef SPH_GCC
5 #if __GNUC__ >= 7
6 #pragma GCC diagnostic ignored "-Wint-in-bool-context"
7 #pragma GCC diagnostic ignored "-Wduplicated-branches"
8 #endif
9 #endif
10 
11 #ifdef SPH_USE_EIGEN
12 #include <Eigen/IterativeLinearSolvers>
13 #include <Eigen/SparseLU>
14 #endif
15 
17 
18 #ifdef SPH_USE_EIGEN
19 
20 class SparseMatrix::Impl {
21 private:
22  Eigen::SparseMatrix<Float> matrix;
24 
25  using SparseVector = Eigen::Matrix<Float, Eigen::Dynamic, 1>;
26 
27 public:
28  Impl(const Size rows, const Size cols)
29  : matrix(rows, cols) {}
30 
31  void insert(const Size i, const Size j, const Float value) {
32  SPH_ASSERT(i < matrix.innerSize() && j < matrix.innerSize(), i, j);
33  triplets.push(Eigen::Triplet<Float>(i, j, value));
34  }
35 
37  const SparseMatrix::Solver solver,
38  const Float tolerance) {
39  SPH_ASSERT(values.size() == matrix.innerSize());
40  // this is takes straigh from Eigen documentation
41  // (http://eigen.tuxfamily.org/dox-devel/group__TopicSparseSystems.html)
42 
43  matrix.setFromTriplets(triplets.begin(), triplets.end());
45 
46  SparseVector b;
47  b.resize(values.size());
48  for (Size i = 0; i < values.size(); ++i) {
49  b(i) = values[i];
50  }
51 
53  switch (solver) {
55  Eigen::SparseLU<Eigen::SparseMatrix<Float>, Eigen::COLAMDOrdering<int>> solver;
56  a = solveImpl(solver, b);
57  break;
58  }
60 #ifdef SPH_DEBUG
61  // check that the matrix is symmetric
62  {
63  const Size n = matrix.innerSize();
64  for (Size i = 0; i < n; ++i) {
65  for (Size j = i; j < n; ++j) {
66  const Float mij = matrix.coeff(i, j);
67  const Float mji = matrix.coeff(j, i);
68  SPH_ASSERT(almostEqual(mij, mji), mij, mji);
69  }
70  }
71  }
72 #endif
73  Eigen::ConjugateGradient<Eigen::SparseMatrix<Float>, Eigen::Lower | Eigen::Upper> solver;
74  if (tolerance > 0._f) {
75  solver.setTolerance(tolerance);
76  }
77  a = solveImpl(solver, b);
78  break;
79  }
81  Eigen::LeastSquaresConjugateGradient<Eigen::SparseMatrix<Float>> solver;
82  if (tolerance > 0._f) {
83  solver.setTolerance(tolerance);
84  }
85  a = solveImpl(solver, b);
86  break;
87  }
89  Eigen::BiCGSTAB<Eigen::SparseMatrix<Float>> solver;
90  if (tolerance > 0._f) {
91  solver.setTolerance(tolerance);
92  }
93  a = solveImpl(solver, b);
94  break;
95  }
96  default:
98  }
99 
100  if (!a) {
101  return makeUnexpected<Array<Float>>(a.error());
102  }
103  Array<Float> result;
104  result.resize(matrix.outerSize());
105  for (Size i = 0; i < result.size(); ++i) {
106  result[i] = a.value()(i);
107  }
108  return Expected<Array<Float>>(std::move(result));
109  }
110 
111 private:
112  template <typename TSolver>
113  Expected<SparseVector> solveImpl(TSolver& solver, const SparseVector& b) {
114  solver.compute(matrix);
115  if (solver.info() != Eigen::Success) {
116  return makeUnexpected<SparseVector>("Decomposition of matrix failed");
117  }
118  SparseVector a;
119  a = solver.solve(b);
120  if (solver.info() != Eigen::Success) {
121  return makeUnexpected<SparseVector>("Equations cannot be solved");
122  }
123  return a;
124  }
125 };
126 
127 SparseMatrix::SparseMatrix() = default;
128 
129 SparseMatrix::SparseMatrix(const Size rows, const Size cols)
130  : impl(makeAuto<Impl>(rows, cols)) {}
131 
132 SparseMatrix::~SparseMatrix() = default;
133 
134 void SparseMatrix::resize(const Size rows, const Size cols) {
135  impl = makeAuto<Impl>(rows, cols);
136 }
137 
138 void SparseMatrix::insert(const Size i, const Size j, const Float value) {
139  impl->insert(i, j, value);
140 }
141 
143  const Solver solver,
144  const Float tolerance) {
145  return impl->solve(values, solver, tolerance);
146 }
147 
148 #endif
149 
INLINE bool almostEqual(const AffineMatrix &m1, const AffineMatrix &m2, const Float eps=EPS)
Definition: AffineMatrix.h:278
#define SPH_ASSERT(x,...)
Definition: Assert.h:94
#define NOT_IMPLEMENTED
Helper macro marking missing implementation.
Definition: Assert.h:100
INLINE AutoPtr< T > makeAuto(TArgs &&... args)
Definition: AutoPtr.h:124
NAMESPACE_SPH_BEGIN
Definition: BarnesHut.cpp:13
uint32_t Size
Integral type used to index arrays (by default).
Definition: Globals.h:16
double Float
Precision used withing the code. Use Float instead of float or double where precision is important.
Definition: Globals.h:13
#define NAMESPACE_SPH_END
Definition: Object.h:12
Sparse matrix with utility functions (a thin wrapper over Eigen3 implementation)
Generic dynamically allocated resizable storage.
Definition: Array.h:43
void resize(const TCounter newSize)
Resizes the array to new size.
Definition: Array.h:215
INLINE Iterator< StorageType > end() noexcept
Definition: Array.h:462
INLINE void push(U &&u)
Adds new element to the end of the array, resizing the array if necessary.
Definition: Array.h:306
INLINE TCounter size() const noexcept
Definition: Array.h:193
INLINE Iterator< StorageType > begin() noexcept
Definition: Array.h:450
Wrapper of type that either contains a value of given type, or an error message.
Definition: Expected.h:25
const Error & error() const
Returns the error message.
Definition: Expected.h:94
Type & value()
Returns the reference to expected value.
Definition: Expected.h:69
void insert(const Size i, const Size j, const Float value)
Adds a values to given element of the matrix.
void resize(const Size rows, const Size cols)
Changes the size of the matrix, removing all previous entries.
Expected< Array< Float > > solve(const Array< Float > &values, const Solver solver, const Float tolerance=0.)
Solver
Solvers of sparse systems.
Definition: SparseMatrix.h:38
@ BICGSTAB
Stabilized bi-conjugate gradient method, can be used for any square matrix.
@ LSCG
Least-square conjugate gradient, can be used for any matrix.
@ LU
LU factorization, precise but very slow for large problems.