| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Copyright Contributors to the OpenVDB Project | ||
| 2 | // SPDX-License-Identifier: MPL-2.0 | ||
| 3 | |||
| 4 | /// @file ConjGradient.h | ||
| 5 | /// @authors D.J. Hill, Peter Cucka | ||
| 6 | /// @brief Preconditioned conjugate gradient solver (solves @e Ax = @e b using | ||
| 7 | /// the conjugate gradient method with one of a selection of preconditioners) | ||
| 8 | |||
| 9 | #ifndef OPENVDB_MATH_CONJGRADIENT_HAS_BEEN_INCLUDED | ||
| 10 | #define OPENVDB_MATH_CONJGRADIENT_HAS_BEEN_INCLUDED | ||
| 11 | |||
| 12 | #include <openvdb/Exceptions.h> | ||
| 13 | #include <openvdb/Types.h> | ||
| 14 | #include <openvdb/util/logging.h> | ||
| 15 | #include <openvdb/util/NullInterrupter.h> | ||
| 16 | #include "Math.h" // for Abs(), isZero(), Max(), Sqrt() | ||
| 17 | #include <tbb/parallel_for.h> | ||
| 18 | #include <tbb/parallel_reduce.h> | ||
| 19 | #include <algorithm> // for std::lower_bound() | ||
| 20 | #include <cassert> | ||
| 21 | #include <cmath> // for std::isfinite() | ||
| 22 | #include <limits> | ||
| 23 | #include <sstream> | ||
| 24 | #include <string> | ||
| 25 | |||
| 26 | |||
| 27 | namespace openvdb { | ||
| 28 | OPENVDB_USE_VERSION_NAMESPACE | ||
| 29 | namespace OPENVDB_VERSION_NAME { | ||
| 30 | namespace math { | ||
| 31 | namespace pcg { | ||
| 32 | |||
| 33 | using SizeType = Index32; | ||
| 34 | |||
| 35 | using SizeRange = tbb::blocked_range<SizeType>; | ||
| 36 | |||
| 37 | template<typename ValueType> class Vector; | ||
| 38 | |||
| 39 | template<typename ValueType, SizeType STENCIL_SIZE> class SparseStencilMatrix; | ||
| 40 | |||
| 41 | template<typename ValueType> class Preconditioner; | ||
| 42 | template<typename MatrixType> class JacobiPreconditioner; | ||
| 43 | template<typename MatrixType> class IncompleteCholeskyPreconditioner; | ||
| 44 | |||
| 45 | /// Information about the state of a conjugate gradient solution | ||
| 46 | struct State { | ||
| 47 | bool success; | ||
| 48 | int iterations; | ||
| 49 | double relativeError; | ||
| 50 | double absoluteError; | ||
| 51 | }; | ||
| 52 | |||
| 53 | |||
| 54 | /// Return default termination conditions for a conjugate gradient solver. | ||
| 55 | template<typename ValueType> | ||
| 56 | inline State | ||
| 57 | terminationDefaults() | ||
| 58 | { | ||
| 59 | State s; | ||
| 60 |
4/8✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
|
9 | s.success = false; |
| 61 | 2 | s.iterations = 50; | |
| 62 |
3/6✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
|
5 | s.relativeError = 1.0e-6; |
| 63 | 2 | s.absoluteError = std::numeric_limits<ValueType>::epsilon() * 100.0; | |
| 64 | return s; | ||
| 65 | } | ||
| 66 | |||
| 67 | |||
| 68 | //////////////////////////////////////// | ||
| 69 | |||
| 70 | |||
| 71 | /// @brief Solve @e Ax = @e b via the preconditioned conjugate gradient method. | ||
| 72 | /// | ||
| 73 | /// @param A a symmetric, positive-definite, @e N x @e N matrix | ||
| 74 | /// @param b a vector of size @e N | ||
| 75 | /// @param x a vector of size @e N | ||
| 76 | /// @param preconditioner a Preconditioner matrix | ||
| 77 | /// @param termination termination conditions given as a State object with the following fields: | ||
| 78 | /// <dl> | ||
| 79 | /// <dt><i>success</i> | ||
| 80 | /// <dd>ignored | ||
| 81 | /// <dt><i>iterations</i> | ||
| 82 | /// <dd>the maximum number of iterations, with or without convergence | ||
| 83 | /// <dt><i>relativeError</i> | ||
| 84 | /// <dd>the relative error ||<i>b</i> − <i>Ax</i>|| / ||<i>b</i>|| | ||
| 85 | /// that denotes convergence | ||
| 86 | /// <dt><i>absoluteError</i> | ||
| 87 | /// <dd>the absolute error ||<i>b</i> − <i>Ax</i>|| that denotes convergence | ||
| 88 | /// | ||
| 89 | /// @throw ArithmeticError if either @a x or @a b is not of the appropriate size. | ||
| 90 | template<typename PositiveDefMatrix> | ||
| 91 | inline State | ||
| 92 | solve( | ||
| 93 | const PositiveDefMatrix& A, | ||
| 94 | const Vector<typename PositiveDefMatrix::ValueType>& b, | ||
| 95 | Vector<typename PositiveDefMatrix::ValueType>& x, | ||
| 96 | Preconditioner<typename PositiveDefMatrix::ValueType>& preconditioner, | ||
| 97 | const State& termination = terminationDefaults<typename PositiveDefMatrix::ValueType>()); | ||
| 98 | |||
| 99 | |||
| 100 | /// @brief Solve @e Ax = @e b via the preconditioned conjugate gradient method. | ||
| 101 | /// | ||
| 102 | /// @param A a symmetric, positive-definite, @e N x @e N matrix | ||
| 103 | /// @param b a vector of size @e N | ||
| 104 | /// @param x a vector of size @e N | ||
| 105 | /// @param preconditioner a Preconditioner matrix | ||
| 106 | /// @param termination termination conditions given as a State object with the following fields: | ||
| 107 | /// <dl> | ||
| 108 | /// <dt><i>success</i> | ||
| 109 | /// <dd>ignored | ||
| 110 | /// <dt><i>iterations</i> | ||
| 111 | /// <dd>the maximum number of iterations, with or without convergence | ||
| 112 | /// <dt><i>relativeError</i> | ||
| 113 | /// <dd>the relative error ||<i>b</i> − <i>Ax</i>|| / ||<i>b</i>|| | ||
| 114 | /// that denotes convergence | ||
| 115 | /// <dt><i>absoluteError</i> | ||
| 116 | /// <dd>the absolute error ||<i>b</i> − <i>Ax</i>|| that denotes convergence | ||
| 117 | /// @param interrupter an object adhering to the util::NullInterrupter interface | ||
| 118 | /// with which computation can be interrupted | ||
| 119 | /// | ||
| 120 | /// @throw ArithmeticError if either @a x or @a b is not of the appropriate size. | ||
| 121 | /// @throw RuntimeError if the computation is interrupted. | ||
| 122 | template<typename PositiveDefMatrix, typename Interrupter> | ||
| 123 | inline State | ||
| 124 | solve( | ||
| 125 | const PositiveDefMatrix& A, | ||
| 126 | const Vector<typename PositiveDefMatrix::ValueType>& b, | ||
| 127 | Vector<typename PositiveDefMatrix::ValueType>& x, | ||
| 128 | Preconditioner<typename PositiveDefMatrix::ValueType>& preconditioner, | ||
| 129 | Interrupter& interrupter, | ||
| 130 | const State& termination = terminationDefaults<typename PositiveDefMatrix::ValueType>()); | ||
| 131 | |||
| 132 | |||
| 133 | //////////////////////////////////////// | ||
| 134 | |||
| 135 | |||
| 136 | /// Lightweight, variable-length vector | ||
| 137 | template<typename T> | ||
| 138 | class Vector | ||
| 139 | { | ||
| 140 | public: | ||
| 141 | using ValueType = T; | ||
| 142 | using Ptr = SharedPtr<Vector>; | ||
| 143 | |||
| 144 | /// Construct an empty vector. | ||
| 145 | Vector(): mData(nullptr), mSize(0) {} | ||
| 146 | /// Construct a vector of @a n elements, with uninitialized values. | ||
| 147 |
12/24✓ Branch 1 taken 1 times.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 11 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 9 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 1 times.
✓ Branch 15 taken 3 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 1 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 1 times.
✓ Branch 20 taken 3 times.
✗ Branch 21 not taken.
✓ Branch 23 taken 3 times.
✗ Branch 24 not taken.
✓ Branch 27 taken 2 times.
✗ Branch 28 not taken.
|
34 | Vector(SizeType n): mData(new T[n]), mSize(n) {} |
| 148 | /// Construct a vector of @a n elements and initialize each element to the given value. | ||
| 149 | 15 | Vector(SizeType n, const ValueType& val): mData(new T[n]), mSize(n) { this->fill(val); } | |
| 150 | |||
| 151 |
18/72✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 9 times.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 9 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 9 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 9 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✓ Branch 27 taken 1 times.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✓ Branch 30 taken 1 times.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✓ Branch 33 taken 1 times.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✓ Branch 36 taken 1 times.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 42 not taken.
✓ Branch 43 taken 2 times.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✓ Branch 49 taken 2 times.
✗ Branch 50 not taken.
✓ Branch 51 taken 1 times.
✓ Branch 52 taken 2 times.
✗ Branch 53 not taken.
✓ Branch 54 taken 1 times.
✗ Branch 55 not taken.
✓ Branch 57 taken 1 times.
✗ Branch 58 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✗ Branch 67 not taken.
✗ Branch 68 not taken.
✓ Branch 72 taken 1 times.
✗ Branch 73 not taken.
✓ Branch 75 taken 1 times.
✗ Branch 76 not taken.
✗ Branch 80 not taken.
✗ Branch 81 not taken.
✗ Branch 83 not taken.
✗ Branch 84 not taken.
✗ Branch 86 not taken.
✗ Branch 87 not taken.
|
43 | ~Vector() { mSize = 0; delete[] mData; mData = nullptr; } |
| 152 | |||
| 153 | /// Deep copy the given vector. | ||
| 154 | Vector(const Vector&); | ||
| 155 | /// Deep copy the given vector. | ||
| 156 | Vector& operator=(const Vector&); | ||
| 157 | |||
| 158 | /// Return the number of elements in this vector. | ||
| 159 |
9/27✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 16 taken 2 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 2 times.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✓ Branch 28 taken 2 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 2 times.
✗ Branch 32 not taken.
✓ Branch 33 taken 7202 times.
✓ Branch 34 taken 3 times.
✗ Branch 35 not taken.
✓ Branch 36 taken 1 times.
✗ Branch 37 not taken.
|
7216 | SizeType size() const { return mSize; } |
| 160 | /// Return @c true if this vector has no elements. | ||
| 161 | bool empty() const { return (mSize == 0); } | ||
| 162 | |||
| 163 | /// @brief Reset this vector to have @a n elements, with uninitialized values. | ||
| 164 | /// @warning All of this vector's existing values will be lost. | ||
| 165 | void resize(SizeType n); | ||
| 166 | |||
| 167 | /// Swap internal storage with another vector, which need not be the same size. | ||
| 168 | void swap(Vector& other) { std::swap(mData, other.mData); std::swap(mSize, other.mSize); } | ||
| 169 | |||
| 170 | /// Set all elements of this vector to @a value. | ||
| 171 | void fill(const ValueType& value); | ||
| 172 | |||
| 173 | //@{ | ||
| 174 | /// @brief Multiply each element of this vector by @a s. | ||
| 175 | template<typename Scalar> void scale(const Scalar& s); | ||
| 176 | template<typename Scalar> Vector& operator*=(const Scalar& s) { this->scale(s); return *this; } | ||
| 177 | //@} | ||
| 178 | |||
| 179 | /// Return the dot product of this vector with the given vector, which must be the same size. | ||
| 180 | ValueType dot(const Vector&) const; | ||
| 181 | |||
| 182 | /// Return the infinity norm of this vector. | ||
| 183 | ValueType infNorm() const; | ||
| 184 | /// Return the L2 norm of this vector. | ||
| 185 | 471 | ValueType l2Norm() const { return Sqrt(this->dot(*this)); } | |
| 186 | |||
| 187 | /// Return @c true if every element of this vector has a finite value. | ||
| 188 | bool isFinite() const; | ||
| 189 | |||
| 190 | /// @brief Return @c true if this vector is equivalent to the given vector | ||
| 191 | /// to within the specified tolerance. | ||
| 192 | template<typename OtherValueType> | ||
| 193 | bool eq(const Vector<OtherValueType>& other, | ||
| 194 | ValueType eps = Tolerance<ValueType>::value()) const; | ||
| 195 | |||
| 196 | /// Return a string representation of this vector. | ||
| 197 | std::string str() const; | ||
| 198 | |||
| 199 | //@{ | ||
| 200 | /// @brief Return the value of this vector's ith element. | ||
| 201 |
20/50✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 1457 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 1457 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 1922 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✓ Branch 17 taken 1922 times.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✓ Branch 20 taken 1457 times.
✗ Branch 21 not taken.
✓ Branch 23 taken 1457 times.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✓ Branch 27 taken 22418 times.
✗ Branch 28 not taken.
✓ Branch 30 taken 29620 times.
✗ Branch 31 not taken.
✓ Branch 33 taken 22418 times.
✗ Branch 34 not taken.
✓ Branch 36 taken 22418 times.
✗ Branch 37 not taken.
✓ Branch 39 taken 22418 times.
✗ Branch 40 not taken.
✓ Branch 42 taken 22418 times.
✗ Branch 43 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✓ Branch 50 taken 12418 times.
✗ Branch 51 not taken.
✓ Branch 53 taken 12418 times.
✗ Branch 54 not taken.
✓ Branch 56 taken 12418 times.
✗ Branch 57 not taken.
✓ Branch 59 taken 12418 times.
✗ Branch 60 not taken.
✓ Branch 62 taken 12418 times.
✗ Branch 63 not taken.
✓ Branch 65 taken 12418 times.
✗ Branch 66 not taken.
|
4078523 | inline T& at(SizeType i) { return mData[i]; } |
| 202 |
1/2✓ Branch 4 taken 7202 times.
✗ Branch 5 not taken.
|
3852433 | inline const T& at(SizeType i) const { return mData[i]; } |
| 203 | inline T& operator[](SizeType i) { return this->at(i); } | ||
| 204 | inline const T& operator[](SizeType i) const { return this->at(i); } | ||
| 205 | //@} | ||
| 206 | |||
| 207 | //@{ | ||
| 208 | /// @brief Return a pointer to this vector's elements. | ||
| 209 | 2825 | inline T* data() { return mData; } | |
| 210 | 708006416 | inline const T* data() const { return mData; } | |
| 211 | inline const T* constData() const { return mData; } | ||
| 212 | //@} | ||
| 213 | |||
| 214 | private: | ||
| 215 | // Functor for use with tbb::parallel_for() | ||
| 216 | template<typename Scalar> struct ScaleOp; | ||
| 217 | struct DeterministicDotProductOp; | ||
| 218 | // Functors for use with tbb::parallel_reduce() | ||
| 219 | template<typename OtherValueType> struct EqOp; | ||
| 220 | struct InfNormOp; | ||
| 221 | struct IsFiniteOp; | ||
| 222 | |||
| 223 | T* mData; | ||
| 224 | SizeType mSize; | ||
| 225 | }; | ||
| 226 | |||
| 227 | using VectorS = Vector<float>; | ||
| 228 | using VectorD = Vector<double>; | ||
| 229 | |||
| 230 | |||
| 231 | //////////////////////////////////////// | ||
| 232 | |||
| 233 | |||
| 234 | /// @brief Sparse, square matrix representing a 3D stencil operator of size @a STENCIL_SIZE | ||
| 235 | /// @details The implementation is a variation on compressed row storage (CRS). | ||
| 236 | template<typename ValueType_, SizeType STENCIL_SIZE> | ||
| 237 | class SparseStencilMatrix | ||
| 238 | { | ||
| 239 | public: | ||
| 240 | using ValueType = ValueType_; | ||
| 241 | using VectorType = Vector<ValueType>; | ||
| 242 | using Ptr = SharedPtr<SparseStencilMatrix>; | ||
| 243 | |||
| 244 | class ConstValueIter; | ||
| 245 | class ConstRow; | ||
| 246 | class RowEditor; | ||
| 247 | |||
| 248 | static const ValueType sZeroValue; | ||
| 249 | |||
| 250 | /// Construct an @a n x @a n matrix with at most @a STENCIL_SIZE nonzero elements per row. | ||
| 251 | SparseStencilMatrix(SizeType n); | ||
| 252 | |||
| 253 | /// Deep copy the given matrix. | ||
| 254 | SparseStencilMatrix(const SparseStencilMatrix&); | ||
| 255 | |||
| 256 | //@{ | ||
| 257 | /// Return the number of rows in this matrix. | ||
| 258 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | SizeType numRows() const { return mNumRows; } |
| 259 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | SizeType size() const { return mNumRows; } |
| 260 | //@} | ||
| 261 | |||
| 262 | /// @brief Set the value at the given coordinates. | ||
| 263 | /// @warning It is not safe to set values in the same row simultaneously | ||
| 264 | /// from multiple threads. | ||
| 265 | void setValue(SizeType row, SizeType col, const ValueType&); | ||
| 266 | |||
| 267 | //@{ | ||
| 268 | /// @brief Return the value at the given coordinates. | ||
| 269 | /// @warning It is not safe to get values from a row while another thread | ||
| 270 | /// is setting values in that row. | ||
| 271 | const ValueType& getValue(SizeType row, SizeType col) const; | ||
| 272 | const ValueType& operator()(SizeType row, SizeType col) const; | ||
| 273 | //@} | ||
| 274 | |||
| 275 | /// Return a read-only view onto the given row of this matrix. | ||
| 276 | ConstRow getConstRow(SizeType row) const; | ||
| 277 | |||
| 278 | /// Return a read/write view onto the given row of this matrix. | ||
| 279 | RowEditor getRowEditor(SizeType row); | ||
| 280 | |||
| 281 | //@{ | ||
| 282 | /// @brief Multiply all elements in the matrix by @a s; | ||
| 283 | template<typename Scalar> void scale(const Scalar& s); | ||
| 284 | template<typename Scalar> | ||
| 285 | SparseStencilMatrix& operator*=(const Scalar& s) { this->scale(s); return *this; } | ||
| 286 | //@} | ||
| 287 | |||
| 288 | /// @brief Multiply this matrix by @a inVec and return the result in @a resultVec. | ||
| 289 | /// @throw ArithmeticError if either @a inVec or @a resultVec is not of size @e N, | ||
| 290 | /// where @e N x @e N is the size of this matrix. | ||
| 291 | template<typename VecValueType> | ||
| 292 | void vectorMultiply(const Vector<VecValueType>& inVec, Vector<VecValueType>& resultVec) const; | ||
| 293 | |||
| 294 | /// @brief Multiply this matrix by the vector represented by the array @a inVec | ||
| 295 | /// and return the result in @a resultVec. | ||
| 296 | /// @warning Both @a inVec and @a resultVec must have at least @e N elements, | ||
| 297 | /// where @e N x @e N is the size of this matrix. | ||
| 298 | template<typename VecValueType> | ||
| 299 | void vectorMultiply(const VecValueType* inVec, VecValueType* resultVec) const; | ||
| 300 | |||
| 301 | /// @brief Return @c true if this matrix is equivalent to the given matrix | ||
| 302 | /// to within the specified tolerance. | ||
| 303 | template<typename OtherValueType> | ||
| 304 | bool eq(const SparseStencilMatrix<OtherValueType, STENCIL_SIZE>& other, | ||
| 305 | ValueType eps = Tolerance<ValueType>::value()) const; | ||
| 306 | |||
| 307 | /// Return @c true if every element of this matrix has a finite value. | ||
| 308 | bool isFinite() const; | ||
| 309 | |||
| 310 | /// Return a string representation of this matrix. | ||
| 311 | std::string str() const; | ||
| 312 | |||
| 313 | private: | ||
| 314 | struct RowData { | ||
| 315 | 70502483 | RowData(ValueType* v, SizeType* c, SizeType& s): mVals(v), mCols(c), mSize(s) {} | |
| 316 | ValueType* mVals; SizeType* mCols; SizeType& mSize; | ||
| 317 | }; | ||
| 318 | |||
| 319 | struct ConstRowData { | ||
| 320 | 1157260973 | ConstRowData(const ValueType* v, const SizeType* c, const SizeType& s): | |
| 321 | 1157260973 | mVals(v), mCols(c), mSize(s) {} | |
| 322 | const ValueType* mVals; const SizeType* mCols; const SizeType& mSize; | ||
| 323 | }; | ||
| 324 | |||
| 325 | /// Base class for row accessors | ||
| 326 | template<typename DataType_ = RowData> | ||
| 327 | class RowBase | ||
| 328 | { | ||
| 329 | public: | ||
| 330 | using DataType = DataType_; | ||
| 331 | |||
| 332 | static SizeType capacity() { return STENCIL_SIZE; } | ||
| 333 | |||
| 334 | 1227763456 | RowBase(const DataType& data): mData(data) {} | |
| 335 | |||
| 336 | 929686646 | bool empty() const { return (mData.mSize == 0); } | |
| 337 | 1889530866 | const SizeType& size() const { return mData.mSize; } | |
| 338 | |||
| 339 | const ValueType& getValue(SizeType columnIdx, bool& active) const; | ||
| 340 | const ValueType& getValue(SizeType columnIdx) const; | ||
| 341 | |||
| 342 | /// Return an iterator over the stored values in this row. | ||
| 343 | ConstValueIter cbegin() const; | ||
| 344 | |||
| 345 | /// @brief Return @c true if this row is equivalent to the given row | ||
| 346 | /// to within the specified tolerance. | ||
| 347 | template<typename OtherDataType> | ||
| 348 | bool eq(const RowBase<OtherDataType>& other, | ||
| 349 | ValueType eps = Tolerance<ValueType>::value()) const; | ||
| 350 | |||
| 351 | /// @brief Return the dot product of this row with the first | ||
| 352 | /// @a vecSize elements of @a inVec. | ||
| 353 | /// @warning @a inVec must have at least @a vecSize elements. | ||
| 354 | template<typename VecValueType> | ||
| 355 | VecValueType dot(const VecValueType* inVec, SizeType vecSize) const; | ||
| 356 | |||
| 357 | /// Return the dot product of this row with the given vector. | ||
| 358 | template<typename VecValueType> | ||
| 359 | VecValueType dot(const Vector<VecValueType>& inVec) const; | ||
| 360 | |||
| 361 | /// Return a string representation of this row. | ||
| 362 | std::string str() const; | ||
| 363 | |||
| 364 | protected: | ||
| 365 | friend class ConstValueIter; | ||
| 366 | |||
| 367 | 6075874694 | const ValueType& value(SizeType i) const { return mData.mVals[i]; } | |
| 368 | 6107290272 | SizeType column(SizeType i) const { return mData.mCols[i]; } | |
| 369 | |||
| 370 | /// @brief Return the array index of the first column index that is | ||
| 371 | /// equal to <i>or greater than</i> the given column index. | ||
| 372 | /// @note If @a columnIdx is larger than any existing column index, | ||
| 373 | /// the return value will point beyond the end of the array. | ||
| 374 | SizeType find(SizeType columnIdx) const; | ||
| 375 | |||
| 376 | DataType mData; | ||
| 377 | }; | ||
| 378 | |||
| 379 | using ConstRowBase = RowBase<ConstRowData>; | ||
| 380 | |||
| 381 | public: | ||
| 382 | /// Iterator over the stored values in a row of this matrix | ||
| 383 | class ConstValueIter | ||
| 384 | { | ||
| 385 | public: | ||
| 386 | const ValueType& operator*() const | ||
| 387 | { | ||
| 388 |
5/10✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 26 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 14169979 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 10592476 times.
✗ Branch 9 not taken.
|
24762517 | if (mData.mSize == 0) return SparseStencilMatrix::sZeroValue; |
| 389 | 24762517 | return mData.mVals[mCursor]; | |
| 390 | } | ||
| 391 | |||
| 392 | 172613470 | SizeType column() const { return mData.mCols[mCursor]; } | |
| 393 | |||
| 394 | 172613496 | void increment() { mCursor++; } | |
| 395 | 172613496 | ConstValueIter& operator++() { increment(); return *this; } | |
| 396 | 197516014 | operator bool() const { return (mCursor < mData.mSize); } | |
| 397 | |||
| 398 | 3577503 | void reset() { mCursor = 0; } | |
| 399 | |||
| 400 | private: | ||
| 401 | friend class SparseStencilMatrix; | ||
| 402 | ConstValueIter(const RowData& d): mData(d.mVals, d.mCols, d.mSize), mCursor(0) {} | ||
| 403 | 21325005 | ConstValueIter(const ConstRowData& d): mData(d), mCursor(0) {} | |
| 404 | |||
| 405 | const ConstRowData mData; | ||
| 406 | SizeType mCursor; | ||
| 407 | }; | ||
| 408 | |||
| 409 | |||
| 410 | /// Read-only accessor to a row of this matrix | ||
| 411 | class ConstRow: public ConstRowBase | ||
| 412 | { | ||
| 413 | public: | ||
| 414 | ConstRow(const ValueType* valueHead, const SizeType* columnHead, const SizeType& rowSize); | ||
| 415 | }; // class ConstRow | ||
| 416 | |||
| 417 | |||
| 418 | /// Read/write accessor to a row of this matrix | ||
| 419 | class RowEditor: public RowBase<> | ||
| 420 | { | ||
| 421 | public: | ||
| 422 | RowEditor(ValueType* valueHead, SizeType* columnHead, SizeType& rowSize, SizeType colSize); | ||
| 423 | |||
| 424 | /// Set the number of entries in this row to zero. | ||
| 425 | void clear(); | ||
| 426 | |||
| 427 | /// @brief Set the value of the entry in the specified column. | ||
| 428 | /// @return the current number of entries stored in this row. | ||
| 429 | SizeType setValue(SizeType column, const ValueType& value); | ||
| 430 | |||
| 431 | //@{ | ||
| 432 | /// @brief Scale all of the entries in this row. | ||
| 433 | template<typename Scalar> void scale(const Scalar&); | ||
| 434 | template<typename Scalar> | ||
| 435 | RowEditor& operator*=(const Scalar& s) { this->scale(s); return *this; } | ||
| 436 | //@} | ||
| 437 | |||
| 438 | private: | ||
| 439 | const SizeType mNumColumns; // used only for bounds checking | ||
| 440 | }; // class RowEditor | ||
| 441 | |||
| 442 | private: | ||
| 443 | // Functors for use with tbb::parallel_for() | ||
| 444 | struct MatrixCopyOp; | ||
| 445 | template<typename VecValueType> struct VecMultOp; | ||
| 446 | template<typename Scalar> struct RowScaleOp; | ||
| 447 | |||
| 448 | // Functors for use with tbb::parallel_reduce() | ||
| 449 | struct IsFiniteOp; | ||
| 450 | template<typename OtherValueType> struct EqOp; | ||
| 451 | |||
| 452 | const SizeType mNumRows; | ||
| 453 | std::unique_ptr<ValueType[]> mValueArray; | ||
| 454 | std::unique_ptr<SizeType[]> mColumnIdxArray; | ||
| 455 | std::unique_ptr<SizeType[]> mRowSizeArray; | ||
| 456 | }; // class SparseStencilMatrix | ||
| 457 | |||
| 458 | |||
| 459 | //////////////////////////////////////// | ||
| 460 | |||
| 461 | |||
| 462 | /// Base class for conjugate gradient preconditioners | ||
| 463 | template<typename T> | ||
| 464 | class Preconditioner | ||
| 465 | { | ||
| 466 | public: | ||
| 467 | using ValueType = T; | ||
| 468 | using Ptr = SharedPtr<Preconditioner>; | ||
| 469 | |||
| 470 | 9 | template<SizeType STENCIL_SIZE> Preconditioner(const SparseStencilMatrix<T, STENCIL_SIZE>&) {} | |
| 471 | virtual ~Preconditioner() = default; | ||
| 472 | |||
| 473 | ✗ | virtual bool isValid() const { return true; } | |
| 474 | |||
| 475 | /// @brief Apply this preconditioner to a residue vector: | ||
| 476 | /// @e z = <i>M</i><sup><small>−1</small></sup><i>r</i> | ||
| 477 | /// @param r residue vector | ||
| 478 | /// @param[out] z preconditioned residue vector | ||
| 479 | virtual void apply(const Vector<T>& r, Vector<T>& z) = 0; | ||
| 480 | }; | ||
| 481 | |||
| 482 | |||
| 483 | //////////////////////////////////////// | ||
| 484 | |||
| 485 | |||
| 486 | namespace internal { | ||
| 487 | |||
| 488 | // Functor for use with tbb::parallel_for() to copy data from one array to another | ||
| 489 | template<typename T> | ||
| 490 | struct CopyOp | ||
| 491 | { | ||
| 492 | 11 | CopyOp(const T* from_, T* to_): from(from_), to(to_) {} | |
| 493 | |||
| 494 | void operator()(const SizeRange& range) const { | ||
| 495 |
6/8✓ Branch 0 taken 10 times.
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 208424 times.
✓ Branch 5 taken 90 times.
✓ Branch 6 taken 3369084 times.
✓ Branch 7 taken 922 times.
|
3578540 | for (SizeType n = range.begin(), N = range.end(); n < N; ++n) to[n] = from[n]; |
| 496 | } | ||
| 497 | |||
| 498 | const T* from; | ||
| 499 | T* to; | ||
| 500 | }; | ||
| 501 | |||
| 502 | |||
| 503 | // Functor for use with tbb::parallel_for() to fill an array with a constant value | ||
| 504 | template<typename T> | ||
| 505 | struct FillOp | ||
| 506 | { | ||
| 507 | 980 | FillOp(T* data_, const T& val_): data(data_), val(val_) {} | |
| 508 | |||
| 509 | void operator()(const SizeRange& range) const { | ||
| 510 |
8/8✓ Branch 0 taken 628072 times.
✓ Branch 1 taken 263 times.
✓ Branch 2 taken 10111654 times.
✓ Branch 3 taken 2899 times.
✓ Branch 4 taken 43822536 times.
✓ Branch 5 taken 9418 times.
✓ Branch 6 taken 687828130 times.
✓ Branch 7 taken 119409 times.
|
742522381 | for (SizeType n = range.begin(), N = range.end(); n < N; ++n) data[n] = val; |
| 511 | } | ||
| 512 | |||
| 513 | T* data; | ||
| 514 | const T val; | ||
| 515 | }; | ||
| 516 | |||
| 517 | |||
| 518 | // Functor for use with tbb::parallel_for() that computes a * x + y | ||
| 519 | template<typename T> | ||
| 520 | struct LinearOp | ||
| 521 | { | ||
| 522 | 1404 | LinearOp(const T& a_, const T* x_, const T* y_, T* out_): a(a_), x(x_), y(y_), out(out_) {} | |
| 523 | |||
| 524 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 261059 times.
|
261059 | void operator()(const SizeRange& range) const { |
| 525 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 261059 times.
|
261059 | if (isExactlyEqual(a, T(1))) { |
| 526 | ✗ | for (SizeType n = range.begin(), N = range.end(); n < N; ++n) out[n] = x[n] + y[n]; | |
| 527 |
2/2✓ Branch 0 taken 1318 times.
✓ Branch 1 taken 259741 times.
|
261059 | } else if (isExactlyEqual(a, T(-1))) { |
| 528 |
2/2✓ Branch 0 taken 3577508 times.
✓ Branch 1 taken 1318 times.
|
3578826 | for (SizeType n = range.begin(), N = range.end(); n < N; ++n) out[n] = -x[n] + y[n]; |
| 529 | } else { | ||
| 530 |
2/2✓ Branch 0 taken 1058425753 times.
✓ Branch 1 taken 259741 times.
|
1058685494 | for (SizeType n = range.begin(), N = range.end(); n < N; ++n) out[n] = a * x[n] + y[n]; |
| 531 | } | ||
| 532 | 261059 | } | |
| 533 | |||
| 534 | const T a, *x, *y; | ||
| 535 | T* out; | ||
| 536 | }; | ||
| 537 | |||
| 538 | } // namespace internal | ||
| 539 | |||
| 540 | |||
| 541 | //////////////////////////////////////// | ||
| 542 | |||
| 543 | |||
| 544 | inline std::ostream& | ||
| 545 | operator<<(std::ostream& os, const State& state) | ||
| 546 | { | ||
| 547 | os << (state.success ? "succeeded with " : "") | ||
| 548 | << "rel. err. " << state.relativeError << ", abs. err. " << state.absoluteError | ||
| 549 | << " after " << state.iterations << " iteration" << (state.iterations == 1 ? "" : "s"); | ||
| 550 | return os; | ||
| 551 | } | ||
| 552 | |||
| 553 | |||
| 554 | //////////////////////////////////////// | ||
| 555 | |||
| 556 | |||
| 557 | template<typename T> | ||
| 558 | inline | ||
| 559 | Vector<T>::Vector(const Vector& other): mData(new T[other.mSize]), mSize(other.mSize) | ||
| 560 | { | ||
| 561 | tbb::parallel_for(SizeRange(0, mSize), | ||
| 562 | internal::CopyOp<T>(/*from=*/other.mData, /*to=*/mData)); | ||
| 563 | } | ||
| 564 | |||
| 565 | |||
| 566 | template<typename T> | ||
| 567 | inline | ||
| 568 | 9 | Vector<T>& Vector<T>::operator=(const Vector<T>& other) | |
| 569 | { | ||
| 570 | // Update the internal storage to the correct size | ||
| 571 | |||
| 572 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if (mSize != other.mSize) { |
| 573 | ✗ | mSize = other.mSize; | |
| 574 | ✗ | delete[] mData; | |
| 575 | ✗ | mData = new T[mSize]; | |
| 576 | } | ||
| 577 | |||
| 578 | // Deep copy the data | ||
| 579 | 18 | tbb::parallel_for(SizeRange(0, mSize), | |
| 580 | 9 | internal::CopyOp<T>(/*from=*/other.mData, /*to=*/mData)); | |
| 581 | |||
| 582 | 9 | return *this; | |
| 583 | } | ||
| 584 | |||
| 585 | |||
| 586 | template<typename T> | ||
| 587 | inline void | ||
| 588 | Vector<T>::resize(SizeType n) | ||
| 589 | { | ||
| 590 | if (n != mSize) { | ||
| 591 | if (mData) delete[] mData; | ||
| 592 | mData = new T[n]; | ||
| 593 | mSize = n; | ||
| 594 | } | ||
| 595 | } | ||
| 596 | |||
| 597 | |||
| 598 | template<typename T> | ||
| 599 | inline void | ||
| 600 | Vector<T>::fill(const ValueType& value) | ||
| 601 | { | ||
| 602 | 951 | tbb::parallel_for(SizeRange(0, mSize), internal::FillOp<T>(mData, value)); | |
| 603 | } | ||
| 604 | |||
| 605 | |||
| 606 | template<typename T> | ||
| 607 | template<typename Scalar> | ||
| 608 | struct Vector<T>::ScaleOp | ||
| 609 | { | ||
| 610 | 7 | ScaleOp(T* data_, const Scalar& s_): data(data_), s(s_) {} | |
| 611 | |||
| 612 | void operator()(const SizeRange& range) const { | ||
| 613 |
4/4✓ Branch 0 taken 223274 times.
✓ Branch 1 taken 57 times.
✓ Branch 2 taken 3354224 times.
✓ Branch 3 taken 848 times.
|
3578403 | for (SizeType n = range.begin(), N = range.end(); n < N; ++n) data[n] *= s; |
| 614 | } | ||
| 615 | |||
| 616 | T* data; | ||
| 617 | const Scalar s; | ||
| 618 | }; | ||
| 619 | |||
| 620 | |||
| 621 | template<typename T> | ||
| 622 | template<typename Scalar> | ||
| 623 | inline void | ||
| 624 | Vector<T>::scale(const Scalar& s) | ||
| 625 | { | ||
| 626 |
8/32✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 2 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 2 times.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✓ Branch 43 taken 1 times.
✗ Branch 44 not taken.
✓ Branch 46 taken 1 times.
✗ Branch 47 not taken.
|
7 | tbb::parallel_for(SizeRange(0, mSize), ScaleOp<Scalar>(mData, s)); |
| 627 | } | ||
| 628 | |||
| 629 | |||
| 630 | template<typename T> | ||
| 631 | struct Vector<T>::DeterministicDotProductOp | ||
| 632 | { | ||
| 633 | 1258 | DeterministicDotProductOp(const T* a_, const T* b_, | |
| 634 | const SizeType binCount_, const SizeType arraySize_, T* reducetmp_): | ||
| 635 | 1258 | a(a_), b(b_), binCount(binCount_), arraySize(arraySize_), reducetmp(reducetmp_) {} | |
| 636 | |||
| 637 | 125800 | void operator()(const SizeRange& range) const | |
| 638 | { | ||
| 639 | 125800 | const SizeType binSize = arraySize / binCount; | |
| 640 | |||
| 641 | // Iterate over bins (array segments) | ||
| 642 |
2/2✓ Branch 0 taken 125800 times.
✓ Branch 1 taken 125800 times.
|
251600 | for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { |
| 643 | 125800 | const SizeType begin = n * binSize; | |
| 644 |
2/2✓ Branch 0 taken 1258 times.
✓ Branch 1 taken 124542 times.
|
125800 | const SizeType end = (n == binCount-1) ? arraySize : begin + binSize; |
| 645 | |||
| 646 | // Compute the partial sum for this array segment | ||
| 647 | T sum = zeroVal<T>(); | ||
| 648 |
2/2✓ Branch 0 taken 1071899673 times.
✓ Branch 1 taken 125800 times.
|
1072025473 | for (SizeType i = begin; i < end; ++i) { sum += a[i] * b[i]; } |
| 649 | // Store the partial sum | ||
| 650 | 125800 | reducetmp[n] = sum; | |
| 651 | } | ||
| 652 | 125800 | } | |
| 653 | |||
| 654 | |||
| 655 | const T* a; | ||
| 656 | const T* b; | ||
| 657 | const SizeType binCount; | ||
| 658 | const SizeType arraySize; | ||
| 659 | T* reducetmp; | ||
| 660 | }; | ||
| 661 | |||
| 662 | template<typename T> | ||
| 663 | inline T | ||
| 664 | 1415 | Vector<T>::dot(const Vector<T>& other) const | |
| 665 | { | ||
| 666 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1415 times.
|
1415 | assert(this->size() == other.size()); |
| 667 | |||
| 668 | const T* aData = this->data(); | ||
| 669 | const T* bData = other.data(); | ||
| 670 | |||
| 671 | SizeType arraySize = this->size(); | ||
| 672 | |||
| 673 | T result = zeroVal<T>(); | ||
| 674 | |||
| 675 |
2/2✓ Branch 0 taken 157 times.
✓ Branch 1 taken 1258 times.
|
1415 | if (arraySize < 1024) { |
| 676 | |||
| 677 | // Compute the dot product in serial for small arrays | ||
| 678 | |||
| 679 |
2/2✓ Branch 0 taken 139090 times.
✓ Branch 1 taken 157 times.
|
139247 | for (SizeType n = 0; n < arraySize; ++n) { |
| 680 | 139090 | result += aData[n] * bData[n]; | |
| 681 | } | ||
| 682 | |||
| 683 | } else { | ||
| 684 | |||
| 685 | // Compute the dot product by segmenting the arrays into | ||
| 686 | // a predetermined number of sub arrays in parallel and | ||
| 687 | // accumulate the finial result in series. | ||
| 688 | |||
| 689 | const SizeType binCount = 100; | ||
| 690 | T partialSums[100]; | ||
| 691 | |||
| 692 | 1258 | tbb::parallel_for(SizeRange(0, binCount), | |
| 693 | DeterministicDotProductOp(aData, bData, binCount, arraySize, partialSums)); | ||
| 694 | |||
| 695 |
2/2✓ Branch 0 taken 125800 times.
✓ Branch 1 taken 1258 times.
|
127058 | for (SizeType n = 0; n < binCount; ++n) { |
| 696 | 125800 | result += partialSums[n]; | |
| 697 | } | ||
| 698 | } | ||
| 699 | |||
| 700 | 1415 | return result; | |
| 701 | } | ||
| 702 | |||
| 703 | |||
| 704 | template<typename T> | ||
| 705 | struct Vector<T>::InfNormOp | ||
| 706 | { | ||
| 707 | 489 | InfNormOp(const T* data_): data(data_) {} | |
| 708 | |||
| 709 | T operator()(const SizeRange& range, T maxValue) const | ||
| 710 | { | ||
| 711 |
2/2✓ Branch 0 taken 361156103 times.
✓ Branch 1 taken 92418 times.
|
361248521 | for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { |
| 712 |
2/2✓ Branch 0 taken 100691 times.
✓ Branch 1 taken 361055412 times.
|
361256794 | maxValue = Max(maxValue, Abs(data[n])); |
| 713 | } | ||
| 714 | 92418 | return maxValue; | |
| 715 | } | ||
| 716 | |||
| 717 | const T* data; | ||
| 718 | }; | ||
| 719 | |||
| 720 | |||
| 721 | template<typename T> | ||
| 722 | inline T | ||
| 723 | 489 | Vector<T>::infNorm() const | |
| 724 | { | ||
| 725 | // Parallelize over the elements of this vector. | ||
| 726 | 489 | T result = tbb::parallel_reduce(SizeRange(0, this->size()), /*seed=*/zeroVal<T>(), | |
| 727 | 2845 | InfNormOp(this->data()), /*join=*/[](T max1, T max2) { return Max(max1, max2); }); | |
| 728 | 489 | return result; | |
| 729 | } | ||
| 730 | |||
| 731 | |||
| 732 | template<typename T> | ||
| 733 | struct Vector<T>::IsFiniteOp | ||
| 734 | { | ||
| 735 | 9 | IsFiniteOp(const T* data_): data(data_) {} | |
| 736 | |||
| 737 | bool operator()(const SizeRange& range, bool finite) const | ||
| 738 | { | ||
| 739 |
2/4✓ Branch 0 taken 71 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 848 times.
✗ Branch 3 not taken.
|
919 | if (finite) { |
| 740 |
4/4✓ Branch 0 taken 223328 times.
✓ Branch 1 taken 71 times.
✓ Branch 2 taken 3354180 times.
✓ Branch 3 taken 848 times.
|
3578427 | for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { |
| 741 |
2/4✓ Branch 0 taken 223328 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3354180 times.
✗ Branch 3 not taken.
|
3577508 | if (!std::isfinite(data[n])) return false; |
| 742 | } | ||
| 743 | } | ||
| 744 | return finite; | ||
| 745 | } | ||
| 746 | |||
| 747 | const T* data; | ||
| 748 | }; | ||
| 749 | |||
| 750 | |||
| 751 | template<typename T> | ||
| 752 | inline bool | ||
| 753 | 9 | Vector<T>::isFinite() const | |
| 754 | { | ||
| 755 | // Parallelize over the elements of this vector. | ||
| 756 | 9 | bool finite = tbb::parallel_reduce(SizeRange(0, this->size()), /*seed=*/true, | |
| 757 | IsFiniteOp(this->data()), | ||
| 758 | 9 | /*join=*/[](bool finite1, bool finite2) { return (finite1 && finite2); }); | |
| 759 | 9 | return finite; | |
| 760 | } | ||
| 761 | |||
| 762 | |||
| 763 | template<typename T> | ||
| 764 | template<typename OtherValueType> | ||
| 765 | struct Vector<T>::EqOp | ||
| 766 | { | ||
| 767 | 2 | EqOp(const T* a_, const OtherValueType* b_, T e): a(a_), b(b_), eps(e) {} | |
| 768 | |||
| 769 | bool operator()(const SizeRange& range, bool equal) const | ||
| 770 | { | ||
| 771 |
1/2✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
|
10 | if (equal) { |
| 772 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 10 times.
|
20 | for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { |
| 773 |
1/2✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
|
10 | if (!isApproxEqual(a[n], b[n], eps)) return false; |
| 774 | } | ||
| 775 | } | ||
| 776 | return equal; | ||
| 777 | } | ||
| 778 | |||
| 779 | const T* a; | ||
| 780 | const OtherValueType* b; | ||
| 781 | const T eps; | ||
| 782 | }; | ||
| 783 | |||
| 784 | |||
| 785 | template<typename T> | ||
| 786 | template<typename OtherValueType> | ||
| 787 | inline bool | ||
| 788 | 2 | Vector<T>::eq(const Vector<OtherValueType>& other, ValueType eps) const | |
| 789 | { | ||
| 790 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (this->size() != other.size()) return false; |
| 791 | 2 | bool equal = tbb::parallel_reduce(SizeRange(0, this->size()), /*seed=*/true, | |
| 792 | EqOp<OtherValueType>(this->data(), other.data(), eps), | ||
| 793 | ✗ | /*join=*/[](bool eq1, bool eq2) { return (eq1 && eq2); }); | |
| 794 | 2 | return equal; | |
| 795 | } | ||
| 796 | |||
| 797 | |||
| 798 | template<typename T> | ||
| 799 | inline std::string | ||
| 800 | Vector<T>::str() const | ||
| 801 | { | ||
| 802 | std::ostringstream ostr; | ||
| 803 | ostr << "["; | ||
| 804 | std::string sep; | ||
| 805 | for (SizeType n = 0, N = this->size(); n < N; ++n) { | ||
| 806 | ostr << sep << (*this)[n]; | ||
| 807 | sep = ", "; | ||
| 808 | } | ||
| 809 | ostr << "]"; | ||
| 810 | return ostr.str(); | ||
| 811 | } | ||
| 812 | |||
| 813 | |||
| 814 | //////////////////////////////////////// | ||
| 815 | |||
| 816 | |||
| 817 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 818 | const ValueType SparseStencilMatrix<ValueType, STENCIL_SIZE>::sZeroValue = zeroVal<ValueType>(); | ||
| 819 | |||
| 820 | |||
| 821 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 822 | inline | ||
| 823 | 58 | SparseStencilMatrix<ValueType, STENCIL_SIZE>::SparseStencilMatrix(SizeType numRows) | |
| 824 | : mNumRows(numRows) | ||
| 825 | 58 | , mValueArray(new ValueType[mNumRows * STENCIL_SIZE]) | |
| 826 |
1/2✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
|
58 | , mColumnIdxArray(new SizeType[mNumRows * STENCIL_SIZE]) |
| 827 |
1/2✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
|
116 | , mRowSizeArray(new SizeType[mNumRows]) |
| 828 | { | ||
| 829 | // Initialize the matrix to a null state by setting the size of each row to zero. | ||
| 830 |
1/4✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
58 | tbb::parallel_for(SizeRange(0, mNumRows), |
| 831 | internal::FillOp<SizeType>(mRowSizeArray.get(), /*value=*/0)); | ||
| 832 | 58 | } | |
| 833 | |||
| 834 | |||
| 835 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 836 | struct SparseStencilMatrix<ValueType, STENCIL_SIZE>::MatrixCopyOp | ||
| 837 | { | ||
| 838 | 2 | MatrixCopyOp(const SparseStencilMatrix& from_, SparseStencilMatrix& to_): | |
| 839 | 2 | from(&from_), to(&to_) {} | |
| 840 | |||
| 841 | 40 | void operator()(const SizeRange& range) const | |
| 842 | { | ||
| 843 | 40 | const ValueType* fromVal = from->mValueArray.get(); | |
| 844 | const SizeType* fromCol = from->mColumnIdxArray.get(); | ||
| 845 | 40 | ValueType* toVal = to->mValueArray.get(); | |
| 846 | SizeType* toCol = to->mColumnIdxArray.get(); | ||
| 847 |
2/2✓ Branch 0 taken 40 times.
✓ Branch 1 taken 40 times.
|
80 | for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { |
| 848 | 40 | toVal[n] = fromVal[n]; | |
| 849 | 40 | toCol[n] = fromCol[n]; | |
| 850 | } | ||
| 851 | 40 | } | |
| 852 | |||
| 853 | const SparseStencilMatrix* from; SparseStencilMatrix* to; | ||
| 854 | }; | ||
| 855 | |||
| 856 | |||
| 857 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 858 | inline | ||
| 859 | 2 | SparseStencilMatrix<ValueType, STENCIL_SIZE>::SparseStencilMatrix(const SparseStencilMatrix& other) | |
| 860 | 2 | : mNumRows(other.mNumRows) | |
| 861 | 2 | , mValueArray(new ValueType[mNumRows * STENCIL_SIZE]) | |
| 862 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | , mColumnIdxArray(new SizeType[mNumRows * STENCIL_SIZE]) |
| 863 |
1/2✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
4 | , mRowSizeArray(new SizeType[mNumRows]) |
| 864 | { | ||
| 865 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | SizeType size = mNumRows * STENCIL_SIZE; |
| 866 | |||
| 867 | // Copy the value and column index arrays from the other matrix to this matrix. | ||
| 868 |
2/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
2 | tbb::parallel_for(SizeRange(0, size), MatrixCopyOp(/*from=*/other, /*to=*/*this)); |
| 869 | |||
| 870 | // Copy the row size array from the other matrix to this matrix. | ||
| 871 |
1/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
2 | tbb::parallel_for(SizeRange(0, mNumRows), |
| 872 | internal::CopyOp<SizeType>(/*from=*/other.mRowSizeArray.get(), /*to=*/mRowSizeArray.get())); | ||
| 873 | 2 | } | |
| 874 | |||
| 875 | |||
| 876 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 877 | inline void | ||
| 878 | 88 | SparseStencilMatrix<ValueType, STENCIL_SIZE>::setValue(SizeType row, SizeType col, | |
| 879 | const ValueType& val) | ||
| 880 | { | ||
| 881 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
|
88 | assert(row < mNumRows); |
| 882 | 88 | this->getRowEditor(row).setValue(col, val); | |
| 883 | 88 | } | |
| 884 | |||
| 885 | |||
| 886 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 887 | inline const ValueType& | ||
| 888 | 28339968 | SparseStencilMatrix<ValueType, STENCIL_SIZE>::getValue(SizeType row, SizeType col) const | |
| 889 | { | ||
| 890 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14169984 times.
|
28339968 | assert(row < mNumRows); |
| 891 | 28339968 | return this->getConstRow(row).getValue(col); | |
| 892 | } | ||
| 893 | |||
| 894 | |||
| 895 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 896 | inline const ValueType& | ||
| 897 | SparseStencilMatrix<ValueType, STENCIL_SIZE>::operator()(SizeType row, SizeType col) const | ||
| 898 | { | ||
| 899 | return this->getValue(row,col); | ||
| 900 | } | ||
| 901 | |||
| 902 | |||
| 903 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 904 | template<typename Scalar> | ||
| 905 | struct SparseStencilMatrix<ValueType, STENCIL_SIZE>::RowScaleOp | ||
| 906 | { | ||
| 907 | 9 | RowScaleOp(SparseStencilMatrix& m, const Scalar& s_): mat(&m), s(s_) {} | |
| 908 | |||
| 909 | 1428 | void operator()(const SizeRange& range) const | |
| 910 | { | ||
| 911 |
2/2✓ Branch 0 taken 3584700 times.
✓ Branch 1 taken 1428 times.
|
3586128 | for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { |
| 912 | 3584700 | RowEditor row = mat->getRowEditor(n); | |
| 913 | row.scale(s); | ||
| 914 | } | ||
| 915 | 1428 | } | |
| 916 | |||
| 917 | SparseStencilMatrix* mat; | ||
| 918 | const Scalar s; | ||
| 919 | }; | ||
| 920 | |||
| 921 | |||
| 922 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 923 | template<typename Scalar> | ||
| 924 | inline void | ||
| 925 | SparseStencilMatrix<ValueType, STENCIL_SIZE>::scale(const Scalar& s) | ||
| 926 | { | ||
| 927 | // Parallelize over the rows in the matrix. | ||
| 928 |
10/32✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 2 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 2 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 2 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 2 times.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✓ Branch 43 taken 1 times.
✗ Branch 44 not taken.
✓ Branch 46 taken 1 times.
✗ Branch 47 not taken.
|
9 | tbb::parallel_for(SizeRange(0, mNumRows), RowScaleOp<Scalar>(*this, s)); |
| 929 | } | ||
| 930 | |||
| 931 | |||
| 932 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 933 | template<typename VecValueType> | ||
| 934 | struct SparseStencilMatrix<ValueType, STENCIL_SIZE>::VecMultOp | ||
| 935 | { | ||
| 936 | 482 | VecMultOp(const SparseStencilMatrix& m, const VecValueType* i, VecValueType* o): | |
| 937 | 482 | mat(&m), in(i), out(o) {} | |
| 938 | |||
| 939 | 70612 | void operator()(const SizeRange& range) const | |
| 940 | { | ||
| 941 |
2/2✓ Branch 0 taken 357585797 times.
✓ Branch 1 taken 70356 times.
|
357663611 | for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { |
| 942 | 357592999 | ConstRow row = mat->getConstRow(n); | |
| 943 | 357592999 | out[n] = row.dot(in, mat->numRows()); | |
| 944 | } | ||
| 945 | 70612 | } | |
| 946 | |||
| 947 | const SparseStencilMatrix* mat; | ||
| 948 | const VecValueType* in; | ||
| 949 | VecValueType* out; | ||
| 950 | }; | ||
| 951 | |||
| 952 | |||
| 953 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 954 | template<typename VecValueType> | ||
| 955 | inline void | ||
| 956 | 475 | SparseStencilMatrix<ValueType, STENCIL_SIZE>::vectorMultiply( | |
| 957 | const Vector<VecValueType>& inVec, Vector<VecValueType>& resultVec) const | ||
| 958 | { | ||
| 959 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 473 times.
|
475 | if (inVec.size() != mNumRows) { |
| 960 | ✗ | OPENVDB_THROW(ArithmeticError, "matrix and input vector have incompatible sizes (" | |
| 961 | << mNumRows << "x" << mNumRows << " vs. " << inVec.size() << ")"); | ||
| 962 | } | ||
| 963 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 473 times.
|
475 | if (resultVec.size() != mNumRows) { |
| 964 | ✗ | OPENVDB_THROW(ArithmeticError, "matrix and result vector have incompatible sizes (" | |
| 965 | << mNumRows << "x" << mNumRows << " vs. " << resultVec.size() << ")"); | ||
| 966 | } | ||
| 967 | |||
| 968 | vectorMultiply(inVec.data(), resultVec.data()); | ||
| 969 | 475 | } | |
| 970 | |||
| 971 | |||
| 972 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 973 | template<typename VecValueType> | ||
| 974 | inline void | ||
| 975 | SparseStencilMatrix<ValueType, STENCIL_SIZE>::vectorMultiply( | ||
| 976 | const VecValueType* inVec, VecValueType* resultVec) const | ||
| 977 | { | ||
| 978 | // Parallelize over the rows in the matrix. | ||
| 979 | 482 | tbb::parallel_for(SizeRange(0, mNumRows), | |
| 980 | VecMultOp<VecValueType>(*this, inVec, resultVec)); | ||
| 981 | } | ||
| 982 | |||
| 983 | |||
| 984 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 985 | template<typename OtherValueType> | ||
| 986 | struct SparseStencilMatrix<ValueType, STENCIL_SIZE>::EqOp | ||
| 987 | { | ||
| 988 | 2 | EqOp(const SparseStencilMatrix& a_, | |
| 989 | const SparseStencilMatrix<OtherValueType, STENCIL_SIZE>& b_, ValueType e): | ||
| 990 | 2 | a(&a_), b(&b_), eps(e) {} | |
| 991 | |||
| 992 | 10 | bool operator()(const SizeRange& range, bool equal) const | |
| 993 | { | ||
| 994 |
1/2✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
|
10 | if (equal) { |
| 995 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 10 times.
|
20 | for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { |
| 996 |
1/2✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
|
10 | if (!a->getConstRow(n).eq(b->getConstRow(n), eps)) return false; |
| 997 | } | ||
| 998 | } | ||
| 999 | return equal; | ||
| 1000 | } | ||
| 1001 | |||
| 1002 | const SparseStencilMatrix* a; | ||
| 1003 | const SparseStencilMatrix<OtherValueType, STENCIL_SIZE>* b; | ||
| 1004 | const ValueType eps; | ||
| 1005 | }; | ||
| 1006 | |||
| 1007 | |||
| 1008 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 1009 | template<typename OtherValueType> | ||
| 1010 | inline bool | ||
| 1011 | 2 | SparseStencilMatrix<ValueType, STENCIL_SIZE>::eq( | |
| 1012 | const SparseStencilMatrix<OtherValueType, STENCIL_SIZE>& other, ValueType eps) const | ||
| 1013 | { | ||
| 1014 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (this->numRows() != other.numRows()) return false; |
| 1015 | 2 | bool equal = tbb::parallel_reduce(SizeRange(0, this->numRows()), /*seed=*/true, | |
| 1016 | EqOp<OtherValueType>(*this, other, eps), | ||
| 1017 | 1 | /*join=*/[](bool eq1, bool eq2) { return (eq1 && eq2); }); | |
| 1018 | 2 | return equal; | |
| 1019 | } | ||
| 1020 | |||
| 1021 | |||
| 1022 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 1023 | struct SparseStencilMatrix<ValueType, STENCIL_SIZE>::IsFiniteOp | ||
| 1024 | { | ||
| 1025 | 2 | IsFiniteOp(const SparseStencilMatrix& m): mat(&m) {} | |
| 1026 | |||
| 1027 | 10 | bool operator()(const SizeRange& range, bool finite) const | |
| 1028 | { | ||
| 1029 |
1/2✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
|
10 | if (finite) { |
| 1030 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 10 times.
|
20 | for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { |
| 1031 | 10 | const ConstRow row = mat->getConstRow(n); | |
| 1032 |
2/2✓ Branch 0 taken 26 times.
✓ Branch 1 taken 10 times.
|
36 | for (ConstValueIter it = row.cbegin(); it; ++it) { |
| 1033 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
|
26 | if (!std::isfinite(*it)) return false; |
| 1034 | } | ||
| 1035 | } | ||
| 1036 | } | ||
| 1037 | return finite; | ||
| 1038 | } | ||
| 1039 | |||
| 1040 | const SparseStencilMatrix* mat; | ||
| 1041 | }; | ||
| 1042 | |||
| 1043 | |||
| 1044 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 1045 | inline bool | ||
| 1046 | 2 | SparseStencilMatrix<ValueType, STENCIL_SIZE>::isFinite() const | |
| 1047 | { | ||
| 1048 | // Parallelize over the rows of this matrix. | ||
| 1049 | 2 | bool finite = tbb::parallel_reduce(SizeRange(0, this->numRows()), /*seed=*/true, | |
| 1050 | ✗ | IsFiniteOp(*this), /*join=*/[](bool finite1, bool finite2) { return (finite1&&finite2); }); | |
| 1051 | 2 | return finite; | |
| 1052 | } | ||
| 1053 | |||
| 1054 | |||
| 1055 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 1056 | inline std::string | ||
| 1057 | SparseStencilMatrix<ValueType, STENCIL_SIZE>::str() const | ||
| 1058 | { | ||
| 1059 | std::ostringstream ostr; | ||
| 1060 | for (SizeType n = 0, N = this->size(); n < N; ++n) { | ||
| 1061 | ostr << n << ": " << this->getConstRow(n).str() << "\n"; | ||
| 1062 | } | ||
| 1063 | return ostr.str(); | ||
| 1064 | } | ||
| 1065 | |||
| 1066 | |||
| 1067 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 1068 | inline typename SparseStencilMatrix<ValueType, STENCIL_SIZE>::RowEditor | ||
| 1069 | 141004966 | SparseStencilMatrix<ValueType, STENCIL_SIZE>::getRowEditor(SizeType i) | |
| 1070 | { | ||
| 1071 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 70502483 times.
|
141004966 | assert(i < mNumRows); |
| 1072 | 14338852 | const SizeType head = i * STENCIL_SIZE; | |
| 1073 | 141004966 | return RowEditor(&mValueArray[head], &mColumnIdxArray[head], mRowSizeArray[i], mNumRows); | |
| 1074 | } | ||
| 1075 | |||
| 1076 | |||
| 1077 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 1078 | inline typename SparseStencilMatrix<ValueType, STENCIL_SIZE>::ConstRow | ||
| 1079 | 2314521946 | SparseStencilMatrix<ValueType, STENCIL_SIZE>::getConstRow(SizeType i) const | |
| 1080 | { | ||
| 1081 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1157260973 times.
|
2314521946 | assert(i < mNumRows); |
| 1082 | 757821594 | const SizeType head = i * STENCIL_SIZE; // index for this row into main storage | |
| 1083 | 2314521946 | return ConstRow(&mValueArray[head], &mColumnIdxArray[head], mRowSizeArray[i]); | |
| 1084 | } | ||
| 1085 | |||
| 1086 | |||
| 1087 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 1088 | template<typename DataType> | ||
| 1089 | inline SizeType | ||
| 1090 | 1859373292 | SparseStencilMatrix<ValueType, STENCIL_SIZE>::RowBase<DataType>::find(SizeType columnIdx) const | |
| 1091 | { | ||
| 1092 |
2/2✓ Branch 0 taken 918946920 times.
✓ Branch 1 taken 10739726 times.
|
1859373292 | if (this->empty()) return mData.mSize; |
| 1093 | |||
| 1094 | // Get a pointer to the first column index that is equal to or greater than the given index. | ||
| 1095 | // (This assumes that the data is sorted by column.) | ||
| 1096 | 1837893840 | const SizeType* colPtr = std::lower_bound(mData.mCols, mData.mCols + mData.mSize, columnIdx); | |
| 1097 | // Return the offset of the pointer from the beginning of the array. | ||
| 1098 | 1837893840 | return static_cast<SizeType>(colPtr - mData.mCols); | |
| 1099 | } | ||
| 1100 | |||
| 1101 | |||
| 1102 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 1103 | template<typename DataType> | ||
| 1104 | inline const ValueType& | ||
| 1105 | SparseStencilMatrix<ValueType, STENCIL_SIZE>::RowBase<DataType>::getValue( | ||
| 1106 | SizeType columnIdx, bool& active) const | ||
| 1107 | { | ||
| 1108 | active = false; | ||
| 1109 | SizeType idx = this->find(columnIdx); | ||
| 1110 | if (idx < this->size() && this->column(idx) == columnIdx) { | ||
| 1111 | active = true; | ||
| 1112 | return this->value(idx); | ||
| 1113 | } | ||
| 1114 | return SparseStencilMatrix::sZeroValue; | ||
| 1115 | } | ||
| 1116 | |||
| 1117 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 1118 | template<typename DataType> | ||
| 1119 | inline const ValueType& | ||
| 1120 | 1640716430 | SparseStencilMatrix<ValueType, STENCIL_SIZE>::RowBase<DataType>::getValue(SizeType columnIdx) const | |
| 1121 | { | ||
| 1122 | 1640716430 | SizeType idx = this->find(columnIdx); | |
| 1123 |
3/4✓ Branch 0 taken 820358215 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 788942637 times.
✓ Branch 3 taken 31415578 times.
|
1640716430 | if (idx < this->size() && this->column(idx) == columnIdx) { |
| 1124 | 1577885274 | return this->value(idx); | |
| 1125 | } | ||
| 1126 | return SparseStencilMatrix::sZeroValue; | ||
| 1127 | } | ||
| 1128 | |||
| 1129 | |||
| 1130 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 1131 | template<typename DataType> | ||
| 1132 | inline typename SparseStencilMatrix<ValueType, STENCIL_SIZE>::ConstValueIter | ||
| 1133 | SparseStencilMatrix<ValueType, STENCIL_SIZE>::RowBase<DataType>::cbegin() const | ||
| 1134 | { | ||
| 1135 | 21325005 | return ConstValueIter(mData); | |
| 1136 | } | ||
| 1137 | |||
| 1138 | |||
| 1139 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 1140 | template<typename DataType> | ||
| 1141 | template<typename OtherDataType> | ||
| 1142 | inline bool | ||
| 1143 | 10 | SparseStencilMatrix<ValueType, STENCIL_SIZE>::RowBase<DataType>::eq( | |
| 1144 | const RowBase<OtherDataType>& other, ValueType eps) const | ||
| 1145 | { | ||
| 1146 |
1/2✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
|
10 | if (this->size() != other.size()) return false; |
| 1147 |
3/4✓ Branch 0 taken 10 times.
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
|
28 | for (ConstValueIter it = cbegin(), oit = other.cbegin(); it || oit; ++it, ++oit) { |
| 1148 |
1/2✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
|
18 | if (it.column() != oit.column()) return false; |
| 1149 |
1/2✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
|
18 | if (!isApproxEqual(*it, *oit, eps)) return false; |
| 1150 | } | ||
| 1151 | 10 | return true; | |
| 1152 | } | ||
| 1153 | |||
| 1154 | |||
| 1155 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 1156 | template<typename DataType> | ||
| 1157 | template<typename VecValueType> | ||
| 1158 | inline VecValueType | ||
| 1159 |
2/2✓ Branch 0 taken 1065587933 times.
✓ Branch 1 taken 8 times.
|
2131175882 | SparseStencilMatrix<ValueType, STENCIL_SIZE>::RowBase<DataType>::dot( |
| 1160 | const VecValueType* inVec, SizeType vecSize) const | ||
| 1161 | { | ||
| 1162 | VecValueType result = zeroVal<VecValueType>(); | ||
| 1163 |
2/2✓ Branch 0 taken 5286932057 times.
✓ Branch 1 taken 1065587941 times.
|
12705039996 | for (SizeType idx = 0, N = std::min(vecSize, this->size()); idx < N; ++idx) { |
| 1164 | 10573864114 | result += static_cast<VecValueType>(this->value(idx) * inVec[this->column(idx)]); | |
| 1165 | } | ||
| 1166 | 2131175882 | return result; | |
| 1167 | } | ||
| 1168 | |||
| 1169 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 1170 | template<typename DataType> | ||
| 1171 | template<typename VecValueType> | ||
| 1172 | inline VecValueType | ||
| 1173 | SparseStencilMatrix<ValueType, STENCIL_SIZE>::RowBase<DataType>::dot( | ||
| 1174 | const Vector<VecValueType>& inVec) const | ||
| 1175 | { | ||
| 1176 | 708002144 | return dot(inVec.data(), inVec.size()); | |
| 1177 | } | ||
| 1178 | |||
| 1179 | |||
| 1180 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 1181 | template<typename DataType> | ||
| 1182 | inline std::string | ||
| 1183 | SparseStencilMatrix<ValueType, STENCIL_SIZE>::RowBase<DataType>::str() const | ||
| 1184 | { | ||
| 1185 | std::ostringstream ostr; | ||
| 1186 | std::string sep; | ||
| 1187 | for (SizeType n = 0, N = this->size(); n < N; ++n) { | ||
| 1188 | ostr << sep << "(" << this->column(n) << ", " << this->value(n) << ")"; | ||
| 1189 | sep = ", "; | ||
| 1190 | } | ||
| 1191 | return ostr.str(); | ||
| 1192 | } | ||
| 1193 | |||
| 1194 | |||
| 1195 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 1196 | inline | ||
| 1197 | SparseStencilMatrix<ValueType, STENCIL_SIZE>::ConstRow::ConstRow( | ||
| 1198 | const ValueType* valueHead, const SizeType* columnHead, const SizeType& rowSize): | ||
| 1199 | ConstRowBase(ConstRowData(const_cast<ValueType*>(valueHead), | ||
| 1200 | 1157260973 | const_cast<SizeType*>(columnHead), const_cast<SizeType&>(rowSize))) | |
| 1201 | { | ||
| 1202 | } | ||
| 1203 | |||
| 1204 | |||
| 1205 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 1206 | inline | ||
| 1207 | 70502483 | SparseStencilMatrix<ValueType, STENCIL_SIZE>::RowEditor::RowEditor( | |
| 1208 | ValueType* valueHead, SizeType* columnHead, SizeType& rowSize, SizeType colSize): | ||
| 1209 | 70502483 | RowBase<>(RowData(valueHead, columnHead, rowSize)), mNumColumns(colSize) | |
| 1210 | { | ||
| 1211 | } | ||
| 1212 | |||
| 1213 | |||
| 1214 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 1215 | inline void | ||
| 1216 | SparseStencilMatrix<ValueType, STENCIL_SIZE>::RowEditor::clear() | ||
| 1217 | { | ||
| 1218 | // Note: since mSize is a reference, this modifies the underlying matrix. | ||
| 1219 | 7155006 | RowBase<>::mData.mSize = 0; | |
| 1220 | } | ||
| 1221 | |||
| 1222 | |||
| 1223 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 1224 | inline SizeType | ||
| 1225 | 218656862 | SparseStencilMatrix<ValueType, STENCIL_SIZE>::RowEditor::setValue( | |
| 1226 | SizeType column, const ValueType& value) | ||
| 1227 | { | ||
| 1228 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 109328431 times.
|
218656862 | assert(column < mNumColumns); |
| 1229 | |||
| 1230 | RowData& data = RowBase<>::mData; | ||
| 1231 | |||
| 1232 | // Get the offset of the first column index that is equal to or greater than | ||
| 1233 | // the column to be modified. | ||
| 1234 | 218656862 | SizeType offset = this->find(column); | |
| 1235 | |||
| 1236 |
4/4✓ Branch 0 taken 59318229 times.
✓ Branch 1 taken 50010202 times.
✓ Branch 2 taken 56178033 times.
✓ Branch 3 taken 3140196 times.
|
218656862 | if (offset < data.mSize && data.mCols[offset] == column) { |
| 1237 | // If the column already exists, just update its value. | ||
| 1238 | 112356066 | data.mVals[offset] = value; | |
| 1239 | 112356066 | return data.mSize; | |
| 1240 | } | ||
| 1241 | |||
| 1242 | // Check that it is safe to add a new column. | ||
| 1243 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 53150398 times.
|
106300796 | assert(data.mSize < this->capacity()); |
| 1244 | |||
| 1245 |
2/2✓ Branch 0 taken 50010202 times.
✓ Branch 1 taken 3140196 times.
|
106300796 | if (offset >= data.mSize) { |
| 1246 | // The new column's index is larger than any existing index. Append the new column. | ||
| 1247 | 100020404 | data.mVals[data.mSize] = value; | |
| 1248 | 100020404 | data.mCols[data.mSize] = column; | |
| 1249 | } else { | ||
| 1250 | // Insert the new column at the computed offset after shifting subsequent columns. | ||
| 1251 |
2/2✓ Branch 0 taken 4811545 times.
✓ Branch 1 taken 3140196 times.
|
15903482 | for (SizeType i = data.mSize; i > offset; --i) { |
| 1252 | 9623090 | data.mVals[i] = data.mVals[i - 1]; | |
| 1253 | 9623090 | data.mCols[i] = data.mCols[i - 1]; | |
| 1254 | } | ||
| 1255 | 6280392 | data.mVals[offset] = value; | |
| 1256 | 6280392 | data.mCols[offset] = column; | |
| 1257 | } | ||
| 1258 | 106300796 | ++data.mSize; | |
| 1259 | |||
| 1260 | 106300796 | return data.mSize; | |
| 1261 | } | ||
| 1262 | |||
| 1263 | |||
| 1264 | template<typename ValueType, SizeType STENCIL_SIZE> | ||
| 1265 | template<typename Scalar> | ||
| 1266 | inline void | ||
| 1267 | SparseStencilMatrix<ValueType, STENCIL_SIZE>::RowEditor::scale(const Scalar& s) | ||
| 1268 | { | ||
| 1269 |
2/2✓ Branch 0 taken 24810396 times.
✓ Branch 1 taken 3584700 times.
|
28395096 | for (int idx = 0, N = this->size(); idx < N; ++idx) { |
| 1270 | 24810396 | RowBase<>::mData.mVals[idx] *= s; | |
| 1271 | } | ||
| 1272 | } | ||
| 1273 | |||
| 1274 | |||
| 1275 | //////////////////////////////////////// | ||
| 1276 | |||
| 1277 | |||
| 1278 | /// Diagonal preconditioner | ||
| 1279 | template<typename MatrixType> | ||
| 1280 | class JacobiPreconditioner: public Preconditioner<typename MatrixType::ValueType> | ||
| 1281 | { | ||
| 1282 | private: | ||
| 1283 | struct InitOp; | ||
| 1284 | struct ApplyOp; | ||
| 1285 | |||
| 1286 | public: | ||
| 1287 | using ValueType = typename MatrixType::ValueType; | ||
| 1288 | using BaseType = Preconditioner<ValueType>; | ||
| 1289 | using VectorType = Vector<ValueType>; | ||
| 1290 | using Ptr = SharedPtr<JacobiPreconditioner>; | ||
| 1291 | |||
| 1292 | 1 | JacobiPreconditioner(const MatrixType& A): BaseType(A), mDiag(A.numRows()) | |
| 1293 | { | ||
| 1294 | // Initialize vector mDiag with the values from the matrix diagonal. | ||
| 1295 |
1/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
1 | tbb::parallel_for(SizeRange(0, A.numRows()), InitOp(A, mDiag.data())); |
| 1296 | 1 | } | |
| 1297 | |||
| 1298 |
2/6✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
2 | ~JacobiPreconditioner() override = default; |
| 1299 | |||
| 1300 | 3 | void apply(const Vector<ValueType>& r, Vector<ValueType>& z) override | |
| 1301 | { | ||
| 1302 | const SizeType size = mDiag.size(); | ||
| 1303 | |||
| 1304 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | assert(r.size() == z.size()); |
| 1305 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | assert(r.size() == size); |
| 1306 | |||
| 1307 | 3 | tbb::parallel_for(SizeRange(0, size), ApplyOp(mDiag.data(), r.data(), z.data())); | |
| 1308 | 3 | } | |
| 1309 | |||
| 1310 | /// Return @c true if all values along the diagonal are finite. | ||
| 1311 | bool isFinite() const { return mDiag.isFinite(); } | ||
| 1312 | |||
| 1313 | private: | ||
| 1314 | // Functor for use with tbb::parallel_for() | ||
| 1315 | struct InitOp | ||
| 1316 | { | ||
| 1317 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | InitOp(const MatrixType& m, ValueType* v): mat(&m), vec(v) {} |
| 1318 | 5 | void operator()(const SizeRange& range) const { | |
| 1319 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
|
10 | for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { |
| 1320 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
|
5 | const ValueType val = mat->getValue(n, n); |
| 1321 | ✗ | assert(!isApproxZero(val, ValueType(0.0001))); | |
| 1322 | 5 | vec[n] = static_cast<ValueType>(1.0 / val); | |
| 1323 | } | ||
| 1324 | 5 | } | |
| 1325 | const MatrixType* mat; ValueType* vec; | ||
| 1326 | }; | ||
| 1327 | |||
| 1328 | // Functor for use with tbb::parallel_reduce() | ||
| 1329 | struct ApplyOp | ||
| 1330 | { | ||
| 1331 | 3 | ApplyOp(const ValueType* x_, const ValueType* y_, ValueType* out_): | |
| 1332 | 3 | x(x_), y(y_), out(out_) {} | |
| 1333 | void operator()(const SizeRange& range) const { | ||
| 1334 |
2/4✓ Branch 0 taken 15 times.
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
30 | for (SizeType n = range.begin(), N = range.end(); n < N; ++n) out[n] = x[n] * y[n]; |
| 1335 | } | ||
| 1336 | const ValueType *x, *y; ValueType* out; | ||
| 1337 | }; | ||
| 1338 | |||
| 1339 | // The Jacobi preconditioner is a diagonal matrix | ||
| 1340 | VectorType mDiag; | ||
| 1341 | }; // class JacobiPreconditioner | ||
| 1342 | |||
| 1343 | |||
| 1344 | /// Preconditioner using incomplete Cholesky factorization | ||
| 1345 | template<typename MatrixType> | ||
| 1346 | class IncompleteCholeskyPreconditioner: public Preconditioner<typename MatrixType::ValueType> | ||
| 1347 | { | ||
| 1348 | private: | ||
| 1349 | struct CopyToLowerOp; | ||
| 1350 | struct TransposeOp; | ||
| 1351 | |||
| 1352 | public: | ||
| 1353 | using ValueType = typename MatrixType::ValueType; | ||
| 1354 | using BaseType = Preconditioner<ValueType>; | ||
| 1355 | using VectorType = Vector<ValueType>; | ||
| 1356 | using Ptr = SharedPtr<IncompleteCholeskyPreconditioner>; | ||
| 1357 | using TriangularMatrix = SparseStencilMatrix<ValueType, 4>; | ||
| 1358 | using TriangleConstRow = typename TriangularMatrix::ConstRow; | ||
| 1359 | using TriangleRowEditor = typename TriangularMatrix::RowEditor; | ||
| 1360 | |||
| 1361 | 8 | IncompleteCholeskyPreconditioner(const MatrixType& matrix) | |
| 1362 | : BaseType(matrix) | ||
| 1363 | , mLowerTriangular(matrix.numRows()) | ||
| 1364 | , mUpperTriangular(matrix.numRows()) | ||
| 1365 |
1/2✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | , mTempVec(matrix.numRows()) |
| 1366 | { | ||
| 1367 | // Size of matrix | ||
| 1368 | const SizeType numRows = mLowerTriangular.numRows(); | ||
| 1369 | |||
| 1370 | // Copy the upper triangular part to the lower triangular part. | ||
| 1371 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | tbb::parallel_for(SizeRange(0, numRows), CopyToLowerOp(matrix, mLowerTriangular)); |
| 1372 | |||
| 1373 | // Build the Incomplete Cholesky Matrix | ||
| 1374 | // | ||
| 1375 | // Algorithm: | ||
| 1376 | // | ||
| 1377 | // for (k = 0; k < size; ++k) { | ||
| 1378 | // A(k,k) = sqrt(A(k,k)); | ||
| 1379 | // for (i = k +1, i < size; ++i) { | ||
| 1380 | // if (A(i,k) == 0) continue; | ||
| 1381 | // A(i,k) = A(i,k) / A(k,k); | ||
| 1382 | // } | ||
| 1383 | // for (j = k+1; j < size; ++j) { | ||
| 1384 | // for (i = j; i < size; ++i) { | ||
| 1385 | // if (A(i,j) == 0) continue; | ||
| 1386 | // A(i,j) -= A(i,k)*A(j,k); | ||
| 1387 | // } | ||
| 1388 | // } | ||
| 1389 | // } | ||
| 1390 | |||
| 1391 | 8 | mPassedCompatibilityCondition = true; | |
| 1392 | |||
| 1393 |
2/2✓ Branch 0 taken 3577503 times.
✓ Branch 1 taken 8 times.
|
3577511 | for (SizeType k = 0; k < numRows; ++k) { |
| 1394 | |||
| 1395 | 3577503 | TriangleConstRow crow_k = mLowerTriangular.getConstRow(k); | |
| 1396 | 3577503 | ValueType diagonalValue = crow_k.getValue(k); | |
| 1397 | |||
| 1398 | // Test if the matrix build has failed. | ||
| 1399 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3577503 times.
|
3577503 | if (diagonalValue < 1.e-5) { |
| 1400 | ✗ | mPassedCompatibilityCondition = false; | |
| 1401 | ✗ | break; | |
| 1402 | } | ||
| 1403 | |||
| 1404 | 3577503 | diagonalValue = Sqrt(diagonalValue); | |
| 1405 | |||
| 1406 | 3577503 | TriangleRowEditor row_k = mLowerTriangular.getRowEditor(k); | |
| 1407 | 3577503 | row_k.setValue(k, diagonalValue); | |
| 1408 | |||
| 1409 | // Exploit the fact that the matrix is symmetric. | ||
| 1410 | 3577503 | typename MatrixType::ConstRow srcRow = matrix.getConstRow(k); | |
| 1411 | typename MatrixType::ConstValueIter citer = srcRow.cbegin(); | ||
| 1412 |
2/2✓ Branch 0 taken 24762455 times.
✓ Branch 1 taken 3577503 times.
|
28339958 | for ( ; citer; ++citer) { |
| 1413 | SizeType ii = citer.column(); | ||
| 1414 |
2/2✓ Branch 0 taken 14169979 times.
✓ Branch 1 taken 10592476 times.
|
24762455 | if (ii < k+1) continue; // look above diagonal |
| 1415 | |||
| 1416 | 10592476 | TriangleRowEditor row_ii = mLowerTriangular.getRowEditor(ii); | |
| 1417 | |||
| 1418 | 10592476 | row_ii.setValue(k, *citer / diagonalValue); | |
| 1419 | } | ||
| 1420 | |||
| 1421 | // for (j = k+1; j < size; ++j) replaced by row iter below | ||
| 1422 | citer.reset(); // k,j entries | ||
| 1423 |
2/2✓ Branch 0 taken 24762455 times.
✓ Branch 1 taken 3577503 times.
|
28339958 | for ( ; citer; ++citer) { |
| 1424 | SizeType j = citer.column(); | ||
| 1425 |
2/2✓ Branch 0 taken 14169979 times.
✓ Branch 1 taken 10592476 times.
|
24762455 | if (j < k+1) continue; |
| 1426 | |||
| 1427 | 10592476 | TriangleConstRow row_j = mLowerTriangular.getConstRow(j); | |
| 1428 | 10592476 | ValueType a_jk = row_j.getValue(k); // a_jk is non zero if a_kj is non zero | |
| 1429 | |||
| 1430 | // Entry (i,j) is non-zero if matrix(j,i) is nonzero | ||
| 1431 | |||
| 1432 | 10592476 | typename MatrixType::ConstRow mask = matrix.getConstRow(j); | |
| 1433 | typename MatrixType::ConstValueIter maskIter = mask.cbegin(); | ||
| 1434 |
2/2✓ Branch 0 taken 73563632 times.
✓ Branch 1 taken 10592476 times.
|
84156108 | for ( ; maskIter; ++maskIter) { |
| 1435 | SizeType i = maskIter.column(); | ||
| 1436 |
2/2✓ Branch 0 taken 31555578 times.
✓ Branch 1 taken 42008054 times.
|
73563632 | if (i < j) continue; |
| 1437 | |||
| 1438 | 42008054 | TriangleConstRow crow_i = mLowerTriangular.getConstRow(i); | |
| 1439 | 42008054 | ValueType a_ij = crow_i.getValue(j); | |
| 1440 | 42008054 | ValueType a_ik = crow_i.getValue(k); | |
| 1441 | 42008054 | TriangleRowEditor row_i = mLowerTriangular.getRowEditor(i); | |
| 1442 | 42008054 | a_ij -= a_ik * a_jk; | |
| 1443 | |||
| 1444 | 42008054 | row_i.setValue(j, a_ij); | |
| 1445 | } | ||
| 1446 | } | ||
| 1447 | } | ||
| 1448 | |||
| 1449 | // Build the transpose of the IC matrix: mUpperTriangular | ||
| 1450 |
1/4✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
8 | tbb::parallel_for(SizeRange(0, numRows), |
| 1451 | TransposeOp(matrix, mLowerTriangular, mUpperTriangular)); | ||
| 1452 | 8 | } | |
| 1453 | |||
| 1454 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
46 | ~IncompleteCholeskyPreconditioner() override = default; |
| 1455 | |||
| 1456 | 7 | bool isValid() const override { return mPassedCompatibilityCondition; } | |
| 1457 | |||
| 1458 | 468 | void apply(const Vector<ValueType>& rVec, Vector<ValueType>& zVec) override | |
| 1459 | { | ||
| 1460 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 468 times.
|
468 | if (!mPassedCompatibilityCondition) { |
| 1461 | ✗ | OPENVDB_THROW(ArithmeticError, "invalid Cholesky decomposition"); | |
| 1462 | } | ||
| 1463 | |||
| 1464 | // Solve mUpperTriangular * mLowerTriangular * rVec = zVec; | ||
| 1465 | |||
| 1466 | SizeType size = mLowerTriangular.numRows(); | ||
| 1467 | |||
| 1468 | zVec.fill(zeroVal<ValueType>()); | ||
| 1469 | ValueType* zData = zVec.data(); | ||
| 1470 | |||
| 1471 |
1/2✓ Branch 0 taken 468 times.
✗ Branch 1 not taken.
|
468 | if (size == 0) return; |
| 1472 | |||
| 1473 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 468 times.
|
468 | assert(rVec.size() == size); |
| 1474 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 468 times.
|
468 | assert(zVec.size() == size); |
| 1475 | |||
| 1476 | // Allocate a temp vector | ||
| 1477 | mTempVec.fill(zeroVal<ValueType>()); | ||
| 1478 | ValueType* tmpData = mTempVec.data(); | ||
| 1479 | const ValueType* rData = rVec.data(); | ||
| 1480 | |||
| 1481 | // Solve mLowerTriangular * tmp = rVec; | ||
| 1482 |
2/2✓ Branch 0 taken 354001072 times.
✓ Branch 1 taken 468 times.
|
354001540 | for (SizeType i = 0; i < size; ++i) { |
| 1483 | 354001072 | typename TriangularMatrix::ConstRow row = mLowerTriangular.getConstRow(i); | |
| 1484 | 354001072 | ValueType diagonal = row.getValue(i); | |
| 1485 | ValueType dot = row.dot(mTempVec); | ||
| 1486 | 354001072 | tmpData[i] = (rData[i] - dot) / diagonal; | |
| 1487 | if (!std::isfinite(tmpData[i])) { | ||
| 1488 | OPENVDB_LOG_DEBUG_RUNTIME("1 diagonal was " << diagonal); | ||
| 1489 | OPENVDB_LOG_DEBUG_RUNTIME("1a diagonal " << row.getValue(i)); | ||
| 1490 | } | ||
| 1491 | } | ||
| 1492 | |||
| 1493 | // Solve mUpperTriangular * zVec = tmp; | ||
| 1494 |
2/2✓ Branch 0 taken 354001072 times.
✓ Branch 1 taken 468 times.
|
354001540 | for (SizeType ii = 0; ii < size; ++ii) { |
| 1495 | 354001072 | SizeType i = size - 1 - ii; | |
| 1496 | 354001072 | typename TriangularMatrix::ConstRow row = mUpperTriangular.getConstRow(i); | |
| 1497 | 354001072 | ValueType diagonal = row.getValue(i); | |
| 1498 | ValueType dot = row.dot(zVec); | ||
| 1499 | 354001072 | zData[i] = (tmpData[i] - dot) / diagonal; | |
| 1500 | if (!std::isfinite(zData[i])) { | ||
| 1501 | OPENVDB_LOG_DEBUG_RUNTIME("2 diagonal was " << diagonal); | ||
| 1502 | } | ||
| 1503 | } | ||
| 1504 | } | ||
| 1505 | |||
| 1506 | const TriangularMatrix& lowerMatrix() const { return mLowerTriangular; } | ||
| 1507 | const TriangularMatrix& upperMatrix() const { return mUpperTriangular; } | ||
| 1508 | |||
| 1509 | private: | ||
| 1510 | // Functor for use with tbb::parallel_for() | ||
| 1511 | struct CopyToLowerOp | ||
| 1512 | { | ||
| 1513 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | CopyToLowerOp(const MatrixType& m, TriangularMatrix& l): mat(&m), lower(&l) {} |
| 1514 | 946 | void operator()(const SizeRange& range) const { | |
| 1515 |
2/2✓ Branch 0 taken 3577503 times.
✓ Branch 1 taken 946 times.
|
3578449 | for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { |
| 1516 | 3577503 | typename TriangularMatrix::RowEditor outRow = lower->getRowEditor(n); | |
| 1517 | outRow.clear(); | ||
| 1518 | 3577503 | typename MatrixType::ConstRow inRow = mat->getConstRow(n); | |
| 1519 |
2/2✓ Branch 0 taken 24762455 times.
✓ Branch 1 taken 3577503 times.
|
28339958 | for (typename MatrixType::ConstValueIter it = inRow.cbegin(); it; ++it) { |
| 1520 |
2/2✓ Branch 0 taken 10592476 times.
✓ Branch 1 taken 14169979 times.
|
24762455 | if (it.column() > n) continue; // skip above diagonal |
| 1521 | 14169979 | outRow.setValue(it.column(), *it); | |
| 1522 | } | ||
| 1523 | } | ||
| 1524 | 946 | } | |
| 1525 | const MatrixType* mat; TriangularMatrix* lower; | ||
| 1526 | }; | ||
| 1527 | |||
| 1528 | // Functor for use with tbb::parallel_for() | ||
| 1529 | struct TransposeOp | ||
| 1530 | { | ||
| 1531 | 8 | TransposeOp(const MatrixType& m, const TriangularMatrix& l, TriangularMatrix& u): | |
| 1532 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | mat(&m), lower(&l), upper(&u) {} |
| 1533 | 971 | void operator()(const SizeRange& range) const { | |
| 1534 |
2/2✓ Branch 0 taken 3577503 times.
✓ Branch 1 taken 971 times.
|
3578474 | for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { |
| 1535 | 3577503 | typename TriangularMatrix::RowEditor outRow = upper->getRowEditor(n); | |
| 1536 | outRow.clear(); | ||
| 1537 | // Use the fact that matrix is symmetric. | ||
| 1538 | 3577503 | typename MatrixType::ConstRow inRow = mat->getConstRow(n); | |
| 1539 |
2/2✓ Branch 0 taken 24762455 times.
✓ Branch 1 taken 3577503 times.
|
28339958 | for (typename MatrixType::ConstValueIter it = inRow.cbegin(); it; ++it) { |
| 1540 | const SizeType column = it.column(); | ||
| 1541 |
2/2✓ Branch 0 taken 10592476 times.
✓ Branch 1 taken 14169979 times.
|
24762455 | if (column < n) continue; // only set upper triangle |
| 1542 | 14169979 | outRow.setValue(column, lower->getValue(column, n)); | |
| 1543 | } | ||
| 1544 | } | ||
| 1545 | 971 | } | |
| 1546 | const MatrixType* mat; const TriangularMatrix* lower; TriangularMatrix* upper; | ||
| 1547 | }; | ||
| 1548 | |||
| 1549 | TriangularMatrix mLowerTriangular; | ||
| 1550 | TriangularMatrix mUpperTriangular; | ||
| 1551 | Vector<ValueType> mTempVec; | ||
| 1552 | bool mPassedCompatibilityCondition; | ||
| 1553 | }; // class IncompleteCholeskyPreconditioner | ||
| 1554 | |||
| 1555 | |||
| 1556 | //////////////////////////////////////// | ||
| 1557 | |||
| 1558 | |||
| 1559 | namespace internal { | ||
| 1560 | |||
| 1561 | /// Compute @e ax + @e y. | ||
| 1562 | template<typename T> | ||
| 1563 | inline void | ||
| 1564 | axpy(const T& a, const T* xVec, const T* yVec, T* resultVec, SizeType size) | ||
| 1565 | { | ||
| 1566 | 1404 | tbb::parallel_for(SizeRange(0, size), LinearOp<T>(a, xVec, yVec, resultVec)); | |
| 1567 | } | ||
| 1568 | |||
| 1569 | /// Compute @e ax + @e y. | ||
| 1570 | template<typename T> | ||
| 1571 | inline void | ||
| 1572 | 1404 | axpy(const T& a, const Vector<T>& xVec, const Vector<T>& yVec, Vector<T>& result) | |
| 1573 | { | ||
| 1574 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1404 times.
|
1404 | assert(xVec.size() == yVec.size()); |
| 1575 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1404 times.
|
1404 | assert(xVec.size() == result.size()); |
| 1576 | axpy(a, xVec.data(), yVec.data(), result.data(), xVec.size()); | ||
| 1577 | 1404 | } | |
| 1578 | |||
| 1579 | |||
| 1580 | /// Compute @e r = @e b − @e Ax. | ||
| 1581 | template<typename MatrixOperator, typename VecValueType> | ||
| 1582 | inline void | ||
| 1583 | 9 | computeResidual(const MatrixOperator& A, const VecValueType* x, | |
| 1584 | const VecValueType* b, VecValueType* r) | ||
| 1585 | { | ||
| 1586 | // Compute r = A * x. | ||
| 1587 | A.vectorMultiply(x, r); | ||
| 1588 | // Compute r = b - r. | ||
| 1589 | 9 | tbb::parallel_for(SizeRange(0, A.numRows()), LinearOp<VecValueType>(-1.0, r, b, r)); | |
| 1590 | 9 | } | |
| 1591 | |||
| 1592 | /// Compute @e r = @e b − @e Ax. | ||
| 1593 | template<typename MatrixOperator, typename T> | ||
| 1594 | inline void | ||
| 1595 | 9 | computeResidual(const MatrixOperator& A, const Vector<T>& x, const Vector<T>& b, Vector<T>& r) | |
| 1596 | { | ||
| 1597 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | assert(x.size() == b.size()); |
| 1598 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | assert(x.size() == r.size()); |
| 1599 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | assert(x.size() == A.numRows()); |
| 1600 | |||
| 1601 | 9 | computeResidual(A, x.data(), b.data(), r.data()); | |
| 1602 | 9 | } | |
| 1603 | |||
| 1604 | } // namespace internal | ||
| 1605 | |||
| 1606 | |||
| 1607 | //////////////////////////////////////// | ||
| 1608 | |||
| 1609 | |||
| 1610 | template<typename PositiveDefMatrix> | ||
| 1611 | inline State | ||
| 1612 | solve( | ||
| 1613 | const PositiveDefMatrix& Amat, | ||
| 1614 | const Vector<typename PositiveDefMatrix::ValueType>& bVec, | ||
| 1615 | Vector<typename PositiveDefMatrix::ValueType>& xVec, | ||
| 1616 | Preconditioner<typename PositiveDefMatrix::ValueType>& precond, | ||
| 1617 | const State& termination) | ||
| 1618 | { | ||
| 1619 | 2 | util::NullInterrupter interrupter; | |
| 1620 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
2 | return solve(Amat, bVec, xVec, precond, interrupter, termination); |
| 1621 | } | ||
| 1622 | |||
| 1623 | |||
| 1624 | template<typename PositiveDefMatrix, typename Interrupter> | ||
| 1625 | inline State | ||
| 1626 | 9 | solve( | |
| 1627 | const PositiveDefMatrix& Amat, | ||
| 1628 | const Vector<typename PositiveDefMatrix::ValueType>& bVec, | ||
| 1629 | Vector<typename PositiveDefMatrix::ValueType>& xVec, | ||
| 1630 | Preconditioner<typename PositiveDefMatrix::ValueType>& precond, | ||
| 1631 | Interrupter& interrupter, | ||
| 1632 | const State& termination) | ||
| 1633 | { | ||
| 1634 | using ValueType = typename PositiveDefMatrix::ValueType; | ||
| 1635 | using VectorType = Vector<ValueType>; | ||
| 1636 | |||
| 1637 | State result; | ||
| 1638 | 9 | result.success = false; | |
| 1639 | 9 | result.iterations = 0; | |
| 1640 | 9 | result.relativeError = 0.0; | |
| 1641 | 9 | result.absoluteError = 0.0; | |
| 1642 | |||
| 1643 | const SizeType size = Amat.numRows(); | ||
| 1644 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if (size == 0) { |
| 1645 | OPENVDB_LOG_WARN("pcg::solve(): matrix has dimension zero"); | ||
| 1646 | return result; | ||
| 1647 | } | ||
| 1648 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if (size != bVec.size()) { |
| 1649 | ✗ | OPENVDB_THROW(ArithmeticError, "A and b have incompatible sizes" | |
| 1650 | << size << "x" << size << " vs. " << bVec.size() << ")"); | ||
| 1651 | } | ||
| 1652 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if (size != xVec.size()) { |
| 1653 | ✗ | OPENVDB_THROW(ArithmeticError, "A and x have incompatible sizes" | |
| 1654 | << size << "x" << size << " vs. " << xVec.size() << ")"); | ||
| 1655 | } | ||
| 1656 | |||
| 1657 | // Temp vectors | ||
| 1658 | VectorType zVec(size); // transformed residual (M^-1 r) | ||
| 1659 | VectorType pVec(size); // search direction | ||
| 1660 | VectorType qVec(size); // A * p | ||
| 1661 | |||
| 1662 | // Compute norm of B (the source) | ||
| 1663 |
1/2✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
|
9 | const ValueType tmp = bVec.infNorm(); |
| 1664 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9 | const ValueType infNormOfB = isZero(tmp) ? ValueType(1) : tmp; |
| 1665 | |||
| 1666 | // Compute rVec: residual = b - Ax. | ||
| 1667 | VectorType rVec(size); // vector of residuals | ||
| 1668 | |||
| 1669 |
1/2✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
|
9 | internal::computeResidual(Amat, xVec, bVec, rVec); |
| 1670 | |||
| 1671 |
2/4✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 9 times.
|
9 | assert(rVec.isFinite()); |
| 1672 | |||
| 1673 | // Normalize the residual norm with the source norm and look for early out. | ||
| 1674 |
1/2✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
|
9 | result.absoluteError = static_cast<double>(rVec.infNorm()); |
| 1675 | 9 | result.relativeError = static_cast<double>(result.absoluteError / infNormOfB); | |
| 1676 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if (result.relativeError <= termination.relativeError) { |
| 1677 | ✗ | result.success = true; | |
| 1678 | ✗ | return result; | |
| 1679 | } | ||
| 1680 | |||
| 1681 | // Iterations of the CG solve | ||
| 1682 | |||
| 1683 | ValueType rDotZPrev(1); // inner product of <z,r> | ||
| 1684 | |||
| 1685 | // Keep track of the minimum error to monitor convergence. | ||
| 1686 | 9 | ValueType minL2Error = std::numeric_limits<ValueType>::max(); | |
| 1687 | ValueType l2Error; | ||
| 1688 | |||
| 1689 | int iteration = 0; | ||
| 1690 |
1/2✓ Branch 0 taken 471 times.
✗ Branch 1 not taken.
|
471 | for ( ; iteration < termination.iterations; ++iteration) { |
| 1691 | |||
| 1692 |
2/4✓ Branch 1 taken 471 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 471 times.
|
471 | if (interrupter.wasInterrupted()) { |
| 1693 | ✗ | OPENVDB_THROW(RuntimeError, "conjugate gradient solver was interrupted"); | |
| 1694 | } | ||
| 1695 | |||
| 1696 | OPENVDB_LOG_DEBUG_RUNTIME("pcg::solve() " << result); | ||
| 1697 | |||
| 1698 | 471 | result.iterations = iteration + 1; | |
| 1699 | |||
| 1700 | // Apply preconditioner to residual | ||
| 1701 | // z_{k} = M^-1 r_{k} | ||
| 1702 |
1/2✓ Branch 1 taken 471 times.
✗ Branch 2 not taken.
|
471 | precond.apply(rVec, zVec); |
| 1703 | |||
| 1704 | // <r,z> | ||
| 1705 |
1/2✓ Branch 1 taken 471 times.
✗ Branch 2 not taken.
|
471 | const ValueType rDotZ = rVec.dot(zVec); |
| 1706 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 471 times.
|
471 | assert(std::isfinite(rDotZ)); |
| 1707 | |||
| 1708 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 462 times.
|
471 | if (0 == iteration) { |
| 1709 | // Initialize | ||
| 1710 |
1/2✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
|
9 | pVec = zVec; |
| 1711 | } else { | ||
| 1712 | 462 | const ValueType beta = rDotZ / rDotZPrev; | |
| 1713 | // p = beta * p + z | ||
| 1714 |
1/2✓ Branch 1 taken 462 times.
✗ Branch 2 not taken.
|
462 | internal::axpy(beta, pVec, zVec, /*result */pVec); |
| 1715 | } | ||
| 1716 | |||
| 1717 | // q_{k} = A p_{k} | ||
| 1718 |
1/2✓ Branch 1 taken 471 times.
✗ Branch 2 not taken.
|
471 | Amat.vectorMultiply(pVec, qVec); |
| 1719 | |||
| 1720 | // alpha = <r_{k-1}, z_{k-1}> / <p_{k},q_{k}> | ||
| 1721 |
1/2✓ Branch 1 taken 471 times.
✗ Branch 2 not taken.
|
471 | const ValueType pAp = pVec.dot(qVec); |
| 1722 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 471 times.
|
471 | assert(std::isfinite(pAp)); |
| 1723 | |||
| 1724 | 471 | const ValueType alpha = rDotZ / pAp; | |
| 1725 | rDotZPrev = rDotZ; | ||
| 1726 | |||
| 1727 | // x_{k} = x_{k-1} + alpha * p_{k} | ||
| 1728 |
1/2✓ Branch 1 taken 471 times.
✗ Branch 2 not taken.
|
471 | internal::axpy(alpha, pVec, xVec, /*result=*/xVec); |
| 1729 | |||
| 1730 | // r_{k} = r_{k-1} - alpha_{k-1} A p_{k} | ||
| 1731 |
2/6✓ Branch 1 taken 471 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 471 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
471 | internal::axpy(-alpha, qVec, rVec, /*result=*/rVec); |
| 1732 | |||
| 1733 | // update tolerances | ||
| 1734 |
2/2✓ Branch 0 taken 99 times.
✓ Branch 1 taken 372 times.
|
471 | l2Error = rVec.l2Norm(); |
| 1735 | 471 | minL2Error = Min(l2Error, minL2Error); | |
| 1736 | |||
| 1737 |
1/2✓ Branch 1 taken 471 times.
✗ Branch 2 not taken.
|
471 | result.absoluteError = static_cast<double>(rVec.infNorm()); |
| 1738 | 471 | result.relativeError = static_cast<double>(result.absoluteError / infNormOfB); | |
| 1739 | |||
| 1740 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 471 times.
|
471 | if (l2Error > 2 * minL2Error) { |
| 1741 | // The solution started to diverge. | ||
| 1742 | ✗ | result.success = false; | |
| 1743 | 9 | break; | |
| 1744 | } | ||
| 1745 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 471 times.
|
471 | if (!std::isfinite(result.absoluteError)) { |
| 1746 | // Total divergence of solution | ||
| 1747 | ✗ | result.success = false; | |
| 1748 | ✗ | break; | |
| 1749 | } | ||
| 1750 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 465 times.
|
471 | if (result.absoluteError <= termination.absoluteError) { |
| 1751 | // Convergence | ||
| 1752 | 6 | result.success = true; | |
| 1753 | 6 | break; | |
| 1754 | } | ||
| 1755 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 462 times.
|
465 | if (result.relativeError <= termination.relativeError) { |
| 1756 | // Convergence | ||
| 1757 | 3 | result.success = true; | |
| 1758 | 3 | break; | |
| 1759 | } | ||
| 1760 | } | ||
| 1761 | OPENVDB_LOG_DEBUG_RUNTIME("pcg::solve() " << result); | ||
| 1762 | |||
| 1763 | return result; | ||
| 1764 | } | ||
| 1765 | |||
| 1766 | } // namespace pcg | ||
| 1767 | } // namespace math | ||
| 1768 | } // namespace OPENVDB_VERSION_NAME | ||
| 1769 | } // namespace openvdb | ||
| 1770 | |||
| 1771 | #endif // OPENVDB_MATH_CONJGRADIENT_HAS_BEEN_INCLUDED | ||
| 1772 |