AUTH's THMMY "Parallel and distributed systems" course assignments.
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 
 
 

805 Zeilen
28 KiB

  1. /**
  2. * \file matrix.hpp
  3. * \brief A matrix abstraction implementation
  4. *
  5. * \author
  6. * Christos Choutouridis AEM:8997
  7. * <cchoutou@ece.auth.gr>
  8. */
  9. #ifndef MATRIX_HPP_
  10. #define MATRIX_HPP_
  11. #include <type_traits>
  12. #include <utility>
  13. #include <algorithm>
  14. #include <vector>
  15. #include <tuple>
  16. namespace mtx {
  17. using std::size_t;
  18. /*
  19. * Small helper to strip types
  20. */
  21. template<typename T>
  22. struct remove_cvref {
  23. typedef std::remove_cv_t<std::remove_reference_t<T>> type;
  24. };
  25. template<typename T>
  26. using remove_cvref_t = typename remove_cvref<T>::type;
  27. /*!
  28. * Enumerator to denote the storage type of the array to use.
  29. */
  30. enum class MatrixType {
  31. DENSE, /*!< Matrix is dense */
  32. SPARSE, /*!< Matrix is sparse */
  33. };
  34. /*!
  35. * Enumerator to denote the storage type of the array to use.
  36. */
  37. enum class MatrixOrder {
  38. COLMAJOR, /*!< Matrix is column major */
  39. ROWMAJOR, /*!< Matrix is row major */
  40. };
  41. /*
  42. * Forward type declarations
  43. */
  44. template<typename MatrixType> struct MatCol;
  45. template<typename MatrixType> struct MatRow;
  46. template<typename MatrixType> struct MatVal;
  47. /*!
  48. * A 2-D matrix functionality over a 1-D array
  49. *
  50. * This is a very thin abstraction layer over a native array.
  51. * This is tested using compiler explorer and our template produce
  52. * almost identical assembly.
  53. *
  54. * The penalty hit we have is due to the fact that we use a one dimension array
  55. * and we have to calculate the actual position from an (i,j) pair.
  56. * The use of 1D array was our intention from the beginning, so the penalty
  57. * was pretty much unavoidable.
  58. *
  59. * \tparam DataType The underling data type of the array
  60. * \tparam IndexType The underling type for the index variables and sizes
  61. * \tparam Type The storage type of the array
  62. * \arg FULL For full matrix
  63. * \arg SYMMETRIC For symmetric matrix (we use only the lower part)
  64. */
  65. template<typename DataType,
  66. typename IndexType = size_t,
  67. MatrixType Type = MatrixType::DENSE,
  68. MatrixOrder Order = MatrixOrder::ROWMAJOR,
  69. bool Symmetric = false>
  70. struct Matrix {
  71. using dataType = DataType; //!< meta:export of underling data type
  72. using indexType = IndexType; //!< meta:export of underling index type
  73. static constexpr MatrixOrder matrixOrder = Order; //!< meta:export of array order
  74. static constexpr MatrixType matrixType = Type; //!< meta:export of array type
  75. static constexpr bool symmetric = Symmetric; //!< meta:export symmetric flag
  76. /*!
  77. * \name Obj lifetime
  78. */
  79. //! @{
  80. //! Construct an empty matrix with dimensions rows x columns
  81. Matrix(IndexType rows = IndexType{}, IndexType columns = IndexType{}) noexcept
  82. : vector_storage_(capacity(rows, columns)),
  83. raw_storage_(nullptr),
  84. use_vector_(true),
  85. rows_(rows),
  86. cols_(columns) {
  87. data_ = vector_storage_.data();
  88. }
  89. //! Construct a matrix by copying existing data with dimensions rows x columns
  90. Matrix(DataType* data, IndexType major_start, IndexType major_length, IndexType minor_length) noexcept
  91. : vector_storage_(),
  92. raw_storage_ (data + major_start * minor_length),
  93. use_vector_ (false) {
  94. if constexpr (Order == MatrixOrder::ROWMAJOR) {
  95. rows_ = major_length;
  96. cols_ = minor_length;
  97. }
  98. else {
  99. rows_ = minor_length;
  100. cols_ = major_length;
  101. }
  102. data_ = raw_storage_;
  103. }
  104. //! Construct a matrix using an initializer list
  105. Matrix(IndexType rows, IndexType columns, std::initializer_list<DataType> list)
  106. : vector_storage_(list),
  107. raw_storage_(nullptr),
  108. use_vector_(true),
  109. rows_(rows),
  110. cols_(columns) {
  111. if (list.size() != capacity(rows, columns)) {
  112. throw std::invalid_argument("Matrix initializer list size does not match matrix dimensions.");
  113. }
  114. data_ = vector_storage_.data();
  115. }
  116. //! move ctor
  117. Matrix(Matrix&& m) noexcept { moves(std::move(m)); }
  118. //! move
  119. Matrix& operator=(Matrix&& m) noexcept { moves(std::move(m)); return *this; }
  120. Matrix(const Matrix& m) = delete; //!< No copy ctor
  121. Matrix& operator=(const Matrix& m) = delete; //!< No copy
  122. //Matrix(const Matrix& m);
  123. //Matrix& operator=(const Matrix& m) { copy(m); }
  124. //! @}
  125. //! \name Data exposure
  126. //! @{
  127. //! Get/Set the size of each dimension
  128. IndexType rows() const noexcept { return rows_; }
  129. IndexType columns() const noexcept { return cols_; }
  130. //! Get the interface size of the Matrix (what appears to be the size)
  131. IndexType size() const {
  132. return rows_ * cols_;
  133. }
  134. //! Set the interface size of the Matrix (what appears to be the size)
  135. IndexType resize(IndexType rows, IndexType columns) {
  136. if (use_vector_) {
  137. rows_ = rows;
  138. cols_ = columns;
  139. vector_storage_.resize(capacity(rows_, cols_));
  140. data_ = vector_storage_.data();
  141. }
  142. return capacity(rows_, cols_);
  143. }
  144. //! Actual memory capacity of the symmetric matrix
  145. static constexpr IndexType capacity(IndexType M, IndexType N) {
  146. if constexpr (Symmetric)
  147. return (M+1)*N/2;
  148. else
  149. return M*N;
  150. }
  151. /*
  152. * virtual 2D accessors
  153. */
  154. DataType get (IndexType i, IndexType j) {
  155. if constexpr (Symmetric) {
  156. auto T = [](size_t i)->size_t { return i*(i+1)/2; }; // Triangular number of i
  157. if constexpr (Order == MatrixOrder::COLMAJOR) {
  158. // In column major we use the lower triangle of the matrix
  159. if (i>=j) return data_[j*rows_ - T(j) + i]; // Lower, use our notation
  160. else return data_[i*rows_ - T(i) + j]; // Upper, use opposite index
  161. }
  162. else {
  163. // In row major we use the upper triangle of the matrix
  164. if (i<=j) return data_[i*cols_ - T(i) + j]; // Upper, use our notation
  165. else return data_[j*cols_ - T(j) + i]; // Lower, use opposite index
  166. }
  167. }
  168. else {
  169. if constexpr (Order == MatrixOrder::COLMAJOR)
  170. return data_[i + j*rows_];
  171. else
  172. return data_[i*cols_ + j];
  173. }
  174. }
  175. /*!
  176. * \fn DataType set(DataType, IndexType, IndexType)
  177. * \param v
  178. * \param i
  179. * \param j
  180. * \return
  181. */
  182. DataType set (DataType v, IndexType i, IndexType j) {
  183. if constexpr (Symmetric) {
  184. auto T = [](size_t i)->size_t { return i*(i+1)/2; }; // Triangular number of i
  185. if constexpr (Order == MatrixOrder::COLMAJOR) {
  186. // In column major we use the lower triangle of the matrix
  187. if (i>=j) return data_[j*rows_ - T(j) + i] = v; // Lower, use our notation
  188. else return data_[i*rows_ - T(i) + j] = v; // Upper, use opposite index
  189. }
  190. else {
  191. // In row major we use the upper triangle of the matrix
  192. if (i<=j) return data_[i*cols_ - T(i) + j] = v; // Upper, use our notation
  193. else return data_[j*cols_ - T(j) + i] = v; // Lower, use opposite index
  194. }
  195. }
  196. else {
  197. if constexpr (Order == MatrixOrder::COLMAJOR)
  198. return data_[i + j*rows_] = v;
  199. else
  200. return data_[i*cols_ + j] = v;
  201. }
  202. }
  203. // DataType operator()(IndexType i, IndexType j) { return get(i, j); }
  204. /*!
  205. * Return a proxy MatVal object with read and write capabilities.
  206. * @param i The row number
  207. * @param j The column number
  208. * @return tHE MatVal object
  209. */
  210. MatVal<Matrix> operator()(IndexType i, IndexType j) noexcept {
  211. return MatVal<Matrix>(this, get(i, j), i, j);
  212. }
  213. // a basic serial iterator support
  214. DataType* data() noexcept { return data_; }
  215. DataType* begin() noexcept { return data_; }
  216. const DataType* begin() const noexcept { return data_; }
  217. DataType* end() noexcept { return data_ + capacity(rows_, cols_); }
  218. const DataType* end() const noexcept { return data_ + capacity(rows_, cols_); }
  219. // IndexType begin_idx() noexcept { return 0; }
  220. // IndexType end_idx() noexcept { return capacity(rows_, cols_); }
  221. const DataType* data() const noexcept { return data_; }
  222. const IndexType begin_idx() const noexcept { return 0; }
  223. const IndexType end_idx() const noexcept { return capacity(rows_, cols_); }
  224. //! @}
  225. /*!
  226. * \name Safe iteration API
  227. *
  228. * This api automates the iteration over the array based on
  229. * MatrixType
  230. */
  231. //! @{
  232. template<typename F, typename... Args>
  233. void for_each_in (IndexType begin, IndexType end, F&& lambda, Args&&... args) {
  234. for (IndexType it=begin ; it<end ; ++it) {
  235. std::forward<F>(lambda)(std::forward<Args>(args)..., it);
  236. }
  237. }
  238. //! @}
  239. //
  240. void swap(Matrix& src) noexcept {
  241. std::swap(vector_storage_, src.vector_storage_);
  242. std::swap(raw_storage_, src.raw_storage_);
  243. std::swap(data_, src.data_);
  244. std::swap(use_vector_, src.use_vector_);
  245. std::swap(rows_, src.rows_);
  246. std::swap(cols_, src.cols_);
  247. }
  248. private:
  249. //! move helper
  250. void moves(Matrix&& src) noexcept {
  251. vector_storage_ = std::move(src.vector_storage_);
  252. raw_storage_ = std::move(src.raw_storage_);
  253. data_ = std::move(src.data_);
  254. use_vector_ = std::move(src.use_vector_);
  255. rows_ = std::move(src.rows_);
  256. cols_ = std::move(src.cols_);
  257. }
  258. // Storage
  259. std::vector<DataType>
  260. vector_storage_; //!< Internal storage (if used).
  261. DataType* raw_storage_; //!< External storage (if used).
  262. DataType* data_; //!< Pointer to active storage.
  263. bool use_vector_; //!< True if using vector storage, false for raw pointer.
  264. IndexType rows_{}; //!< the virtual size of rows.
  265. IndexType cols_{}; //!< the virtual size of columns.
  266. };
  267. /**
  268. * A simple sparse matrix specialization.
  269. *
  270. * We use CSC format and provide get/set functionalities for each (i,j) item
  271. * on the matrix. We also provide a () overload using a proxy MatVal object.
  272. * This way the user can:
  273. * \code
  274. * auto v = A(3,4);
  275. * A(3, 4) = 7;
  276. * \endcode
  277. *
  278. * We also provide getCol() and getRow() functions witch return a viewer/iterator to rows and
  279. * columns of the matrix. In the case of a symmetric matrix instead of a row we return the
  280. * equivalent column. This way we gain speed due to CSC format nature.
  281. *
  282. * @tparam DataType The type for values
  283. * @tparam IndexType The type for indexes
  284. * @tparam Type The Matrix type (FULL or SYMMETRIC)
  285. */
  286. template<typename DataType, typename IndexType,
  287. MatrixOrder Order,
  288. bool Symmetric>
  289. struct Matrix<DataType, IndexType, MatrixType::SPARSE, Order, Symmetric> {
  290. using dataType = DataType; //!< meta:export of underling data type
  291. using indexType = IndexType; //!< meta:export of underling index type
  292. static constexpr MatrixOrder matrixOrder = Order; //!< meta:export of array order
  293. static constexpr MatrixType matrixType = MatrixType::SPARSE; //!< meta:export of array type
  294. static constexpr bool symmetric = Symmetric; //!< meta:export symmetric flag
  295. friend struct MatCol<Matrix>;
  296. friend struct MatRow<Matrix>;
  297. friend struct MatVal<Matrix>;
  298. /*!
  299. * \name Obj lifetime
  300. */
  301. //! @{
  302. //! Default ctor with optional memory allocations
  303. Matrix(IndexType n=IndexType{}) noexcept:
  304. values{},
  305. rows{},
  306. col_ptr((n)? n+1:2, IndexType{}),
  307. N(n),
  308. NNZ(0) { }
  309. //! A ctor using csc array data
  310. Matrix(IndexType n, IndexType nnz, const IndexType* row, const IndexType* col) noexcept:
  311. values(nnz, 1),
  312. rows(row, row+nnz),
  313. col_ptr(col, col+n+1),
  314. N(n),
  315. NNZ(nnz) { }
  316. //! ctor using csc array data with value array
  317. Matrix(IndexType n, IndexType nnz, const DataType* v, const IndexType* row, const IndexType* col) noexcept:
  318. values(v, v+nnz),
  319. rows(row, row+nnz),
  320. col_ptr(col, col+n+1),
  321. N(n),
  322. NNZ(nnz) { }
  323. //! ctor vectors of row/col and default value for values array
  324. Matrix(IndexType n, IndexType nnz, const DataType v,
  325. const std::vector<IndexType>& row, const std::vector<IndexType>& col) noexcept:
  326. values(nnz, v),
  327. rows (row),
  328. col_ptr(col),
  329. N(n),
  330. NNZ(nnz) { }
  331. //! move ctor
  332. Matrix(Matrix&& m) noexcept { moves(std::move(m)); }
  333. //! move
  334. Matrix& operator=(Matrix&& m) noexcept { moves(std::move(m)); return *this; }
  335. Matrix(const Matrix& m) = delete; //!< make sure there are no copies
  336. Matrix& operator=(const Matrix& m) = delete; //!< make sure there are no copies
  337. //! @}
  338. //! \name Data exposure
  339. //! @{
  340. //! \return the dimension of the matrix
  341. IndexType size() noexcept { return N; }
  342. //! After construction size configuration tool
  343. IndexType resize(IndexType n) {
  344. col_ptr.resize(n+1);
  345. return N = n;
  346. }
  347. //! \return the NNZ of the matrix
  348. IndexType capacity() noexcept { return NNZ; }
  349. //! After construction NNZ size configuration tool
  350. IndexType capacity(IndexType nnz) noexcept {
  351. values.reserve(nnz);
  352. rows.reserve(nnz);
  353. return NNZ;
  354. }
  355. // getters for row arrays of the struct (unused)
  356. std::vector<DataType>& getValues() noexcept { return values; }
  357. std::vector<IndexType>& getRows() noexcept { return rows; }
  358. std::vector<IndexType>& getCols() noexcept { return col_ptr; }
  359. /*!
  360. * Return a proxy MatVal object with read and write capabilities.
  361. * @param i The row number
  362. * @param j The column number
  363. * @return tHE MatVal object
  364. */
  365. MatVal<Matrix> operator()(IndexType i, IndexType j) noexcept {
  366. return MatVal<Matrix>(this, get(i, j), i, j);
  367. }
  368. /*!
  369. * A read item functionality using binary search to find the correct row
  370. *
  371. * @param i The row number
  372. * @param j The column number
  373. * @return The value of the item or DataType{} if is not present.
  374. */
  375. DataType get(IndexType i, IndexType j) noexcept {
  376. IndexType idx; bool found;
  377. std::tie(idx, found) =find_idx(rows, col_ptr[j], col_ptr[j+1], i);
  378. return (found) ? values[idx] : 0;
  379. }
  380. /*!
  381. * A write item functionality.
  382. *
  383. * First we search if the matrix has already a value in (i, j) position.
  384. * If so we just change it to a new value. If not we add the item on the matrix.
  385. *
  386. * @note
  387. * When change a value, we don't increase the NNZ value of the struct. We expect the user has already
  388. * change the NNZ value to the right one using @see capacity() function. When adding a value we
  389. * increase the NNZ.
  390. *
  391. * @param i The row number
  392. * @param j The column number
  393. * @return The new value of the item .
  394. */
  395. DataType set(DataType v, IndexType i, IndexType j) {
  396. IndexType idx; bool found;
  397. std::tie(idx, found) = find_idx(rows, col_ptr[j], col_ptr[j+1], i);
  398. if (found)
  399. return values[idx] = v; // we don't change NNZ even if we write "0"
  400. else {
  401. values.insert(values.begin()+idx, v);
  402. rows.insert(rows.begin()+idx, i);
  403. std::transform(col_ptr.begin()+j+1, col_ptr.end(), col_ptr.begin()+j+1, [](IndexType it) {
  404. return ++it;
  405. });
  406. ++NNZ; // we increase the NNZ even if we write "0"
  407. return v;
  408. }
  409. }
  410. /*!
  411. * Get a view of a CSC column
  412. * @param j The column to get
  413. * @return The MatCol object @see MatCol
  414. */
  415. MatCol<Matrix> getCol(IndexType j) noexcept {
  416. return MatCol<Matrix>(this, col_ptr[j], col_ptr[j+1]);
  417. }
  418. /*!
  419. * Get a view of a CSC row
  420. *
  421. * In case of a SYMMETRIC matrix we can return a column instead.
  422. *
  423. * @param j The row to get
  424. * @return On symmetric matrix MatCol otherwise a MatRow
  425. */
  426. MatCol<Matrix> getRow(IndexType i) noexcept {
  427. if constexpr (Symmetric)
  428. return getCol(i);
  429. else
  430. return MatRow<Matrix>(this, i);
  431. }
  432. // values only iterator support
  433. DataType* begin() noexcept { return values.begin(); }
  434. DataType* end() noexcept { return values.end(); }
  435. //! @}
  436. //! A small iteration helper
  437. template<typename F, typename... Args>
  438. void for_each_in (IndexType begin, IndexType end, F&& lambda, Args&&... args) {
  439. for (IndexType it=begin ; it<end ; ++it) {
  440. std::forward<F>(lambda)(std::forward<Args>(args)..., it);
  441. }
  442. }
  443. private:
  444. /*!
  445. * A small binary search implementation using index for begin-end instead of iterators.
  446. *
  447. * \param v Reference to vector to search
  448. * \param begin The vector's index to begin
  449. * \param end The vector's index to end
  450. * \param match What to search
  451. * \return An <index, status> pair.
  452. * index is the index of the item or end if not found
  453. * status is true if found, false otherwise
  454. */
  455. std::pair<IndexType, bool> find_idx(const std::vector<IndexType>& v, IndexType begin, IndexType end, IndexType match) {
  456. if (v.capacity() != 0 && begin < end) {
  457. IndexType b = begin, e = end-1;
  458. while (b <= e) {
  459. IndexType m = (b+e)/2;
  460. if (v[m] == match) return std::make_pair(m, true);
  461. else if (b >= e) return std::make_pair(end, false);
  462. else {
  463. if (v[m] < match) b = m +1;
  464. else e = m -1;
  465. }
  466. }
  467. }
  468. return std::make_pair(end, false);
  469. }
  470. // move helper
  471. void moves(Matrix&& src) noexcept {
  472. values = std::move(src.values);
  473. rows = std::move(src.rows);
  474. col_ptr = std::move(src.col_ptr);
  475. N = std::move(src.N); // redundant for primitives
  476. NNZ = std::move(src.NNZ); //
  477. }
  478. //! \name Data
  479. //! @{
  480. std::vector<DataType> values {}; //!< vector to store the values of the matrix
  481. std::vector<IndexType> rows{}; //!< vector to store the row information
  482. std::vector<IndexType> col_ptr{1,0}; //!< vector to store the column pointers
  483. IndexType N{0}; //!< The dimension of the matrix (square)
  484. IndexType NNZ{0}; //!< The NNZ (capacity of the matrix)
  485. //! @}
  486. };
  487. /*!
  488. * A view/iterator hybrid object for Matrix columns.
  489. *
  490. * This object provides access to a column of a Matrix. The public functionalities
  491. * allow data access using indexes instead of iterators. We prefer indexes over iterators
  492. * because we can apply the same index to different inner vector of Matrix without conversion.
  493. *
  494. * @tparam DataType
  495. * @tparam IndexType
  496. */
  497. template<typename MatrixType>
  498. struct MatCol {
  499. using owner_t = MatrixType;
  500. using DataType = typename MatrixType::dataType;
  501. using IndexType = typename MatrixType::indexType;
  502. /*!
  503. * ctor using column pointers for begin-end. own is pointer to Matrix.
  504. */
  505. MatCol(owner_t* own, const IndexType begin, const IndexType end) noexcept :
  506. owner_(own), index_(begin), begin_(begin), end_(end) {
  507. vindex_ = vIndexCalc(index_);
  508. }
  509. MatCol() = default;
  510. MatCol(const MatCol&) = delete; //!< make sure there are no copies
  511. MatCol& operator=(const MatCol&)= delete; //!< make sure there are no copies
  512. MatCol(MatCol&&) = default;
  513. MatCol& operator=(MatCol&&) = default;
  514. //! a simple dereference operator, like an iterator
  515. DataType operator* () {
  516. return get();
  517. }
  518. //! Increment operator acts on index(), like an iterator
  519. MatCol& operator++ () { advance(); return *this; }
  520. MatCol& operator++ (int) { MatCol& p = *this; advance(); return p; }
  521. //! () operator acts as member access (like a view)
  522. DataType operator()(IndexType x) {
  523. return (x == index())? get() : DataType{};
  524. }
  525. //! = operator acts as member assignment (like a view)
  526. DataType operator= (DataType v) { return owner_->values[index_] = v; }
  527. // iterator like handlers
  528. // these return a virtual index value based on the items position on the full matrix
  529. // but the move of the index is just a ++ away.
  530. IndexType index() noexcept { return vindex_; }
  531. const IndexType index() const noexcept { return vindex_; }
  532. IndexType begin() noexcept { return vIndexCalc(begin_); }
  533. const IndexType begin() const noexcept { return vIndexCalc(begin_); }
  534. IndexType end() noexcept { return owner_->N; }
  535. const IndexType end() const noexcept { return owner_->N; }
  536. /*!
  537. * Multiplication operator
  538. *
  539. * We follow only the non-zero values and multiply only the common indexes.
  540. *
  541. * @tparam C Universal reference for the type right half site column
  542. *
  543. * @param c The right hand site matrix
  544. * @return The value of the inner product of two vectors
  545. * @note The time complexity is \$ O(nnz1+nnz2) \$.
  546. * Where the nnz is the max NNZ elements of the column of the matrix
  547. */
  548. template <typename C>
  549. DataType operator* (C&& c) {
  550. static_assert(std::is_same<remove_cvref_t<C>, MatCol<MatrixType>>(), "");
  551. DataType v{};
  552. while (index() != end() && c.index() != c.end()) {
  553. if (index() < c.index()) advance(); // advance me
  554. else if (index() > c.index()) ++c; // advance other
  555. else { //index() == c.index()
  556. v += get() * *c; // multiply and advance both
  557. ++c;
  558. advance();
  559. }
  560. }
  561. return v;
  562. }
  563. private:
  564. //! small tool to increase the index pointers to Matrix
  565. void advance() noexcept {
  566. ++index_;
  567. vindex_ = vIndexCalc(index_);
  568. }
  569. //! tool to translate between col_ptr indexes and Matrix "virtual" full matrix indexes
  570. IndexType vIndexCalc(IndexType idx) {
  571. return (idx < end_) ? owner_->rows[idx] : end();
  572. }
  573. //! small get tool
  574. DataType get() { return owner_->values[index_]; }
  575. owner_t* owner_ {nullptr}; //!< Pointer to owner Matrix. MatCol is just a view
  576. IndexType vindex_ {IndexType{}}; //!< Virtual index of full matrix
  577. IndexType index_ {IndexType{}}; //!< index to Matrix::rows
  578. IndexType begin_ {IndexType{}}; //!< beginning index of the column in Matrix::rows
  579. IndexType end_ {IndexType{}}; //!< ending index of the column in Matrix::rows
  580. };
  581. /*!
  582. * A view/iterator hybrid object for Matrix rows.
  583. *
  584. * This object provides access to a column of a Matrix. The public functionalities
  585. * allow data access using indexes instead of iterators. We prefer indexes over iterators
  586. * because we can apply the same index to different inner vector of Matrix without conversion.
  587. *
  588. * @tparam DataType
  589. * @tparam IndexType
  590. */
  591. template<typename MatrixType>
  592. struct MatRow {
  593. using owner_t = MatrixType;
  594. using DataType = typename MatrixType::dataType;
  595. using IndexType = typename MatrixType::indexType;
  596. /*!
  597. * ctor using virtual full matrix row index. own is pointer to Matrix.
  598. */
  599. MatRow(owner_t* own, const IndexType row) noexcept :
  600. owner_(own), vindex_(IndexType{}), row_(row), index_(IndexType{}),
  601. begin_(IndexType{}), end_(owner_->NNZ) {
  602. // place begin
  603. while(begin_ != end_ && owner_->rows[begin_] != row_)
  604. ++begin_;
  605. // place index_ and vindex_
  606. if (owner_->rows[index_] != row_)
  607. advance();
  608. }
  609. MatRow() = default;
  610. MatRow(const MatRow&) = delete; //!< make sure there are no copies
  611. MatRow& operator=(const MatRow&)= delete; //!< make sure there are no copies
  612. MatRow(MatRow&&) = default;
  613. MatRow& operator=(MatRow&&) = default;
  614. //! a simple dereference operator, like an iterator
  615. DataType operator* () {
  616. return get();
  617. }
  618. //! Increment operator acts on index(), like an iterator
  619. //! here the increment is a O(N) process.
  620. MatRow& operator++ () { advance(); return *this; }
  621. MatRow& operator++ (int) { MatRow& p = *this; advance(); return p; }
  622. //! () operator acts as member access (like a view)
  623. DataType operator()(IndexType x) {
  624. return (x == index())? get() : DataType{};
  625. }
  626. //! = operator acts as member assignment (like a view)
  627. DataType operator= (DataType v) { return owner_->values[index_] = v; }
  628. // iterator like handlers
  629. // these return a virtual index value based on the items position on the full matrix
  630. // but the move of the index is just a ++ away.
  631. IndexType index() noexcept { return vindex_; }
  632. const IndexType index() const noexcept { return vindex_; }
  633. IndexType begin() noexcept { return vIndexCalc(begin_); }
  634. const IndexType begin() const noexcept { return vIndexCalc(begin_); }
  635. IndexType end() noexcept { return owner_->N; }
  636. const IndexType end() const noexcept { return owner_->N; }
  637. /*!
  638. * Multiplication operator
  639. *
  640. * We follow only the non-zero values and multiply only the common indexes.
  641. *
  642. * @tparam C Universal reference for the type right half site column
  643. *
  644. * @param c The right hand site matrix
  645. * @return The value of the inner product of two vectors
  646. * @note The time complexity is \$ O(N+nnz2) \$ and way heavier the ColxCol multiplication.
  647. * Where the nnz is the max NNZ elements of the column of the matrix
  648. */
  649. template <typename C>
  650. DataType operator* (C&& c) {
  651. static_assert(std::is_same<remove_cvref_t<C>, MatCol<MatrixType>>(), "");
  652. DataType v{};
  653. while (index() != end() && c.index() != c.end()) {
  654. if (index() < c.index()) advance(); // advance me
  655. else if (index() > c.index()) ++c; // advance other
  656. else { //index() == c.index()
  657. v += get() * *c; // multiply and advance both
  658. ++c;
  659. advance();
  660. }
  661. }
  662. return v;
  663. }
  664. private:
  665. //! small tool to increase the index pointers to Matrix matrix
  666. //! We have to search the entire rows vector in Matrix to find the next
  667. //! virtual row position.
  668. //! time complexity O(N)
  669. void advance() noexcept {
  670. do
  671. ++index_;
  672. while(index_ != end_ && owner_->rows[index_] != row_);
  673. vindex_ = vIndexCalc(index_);
  674. }
  675. //! tool to translate between col_ptr indexes and Matrix "virtual" full matrix indexes
  676. IndexType vIndexCalc(IndexType idx) {
  677. for(IndexType i =0 ; i<(owner_->N+1) ; ++i)
  678. if (idx < owner_->col_ptr[i])
  679. return i-1;
  680. return end();
  681. }
  682. //! small get tool
  683. DataType get() { return owner_->values[index_]; }
  684. owner_t* owner_ {nullptr}; //!< Pointer to owner Matrix. MatCol is just a view
  685. IndexType vindex_ {IndexType{}}; //!< Virtual index of full matrix
  686. IndexType row_ {IndexType{}}; //!< The virtual full matrix row of the object
  687. IndexType index_ {IndexType{}}; //!< index to Matrix::rows
  688. IndexType begin_ {IndexType{}}; //!< beginning index of the column in Matrix::rows
  689. IndexType end_ {IndexType{}}; //!< ending index of the column in Matrix::rows
  690. };
  691. /*!
  692. * A proxy Matrix value object/view.
  693. *
  694. * This object acts as proxy to provide read/write access to an Matrix item.
  695. *
  696. * @tparam DataType The type of the values of the Matrix matrix
  697. * @tparam IndexType The type of the indexes of the Matrix matrix
  698. */
  699. template<typename MatrixType>
  700. struct MatVal {
  701. using owner_t = MatrixType;
  702. using DataType = typename MatrixType::dataType;
  703. using IndexType = typename MatrixType::indexType;
  704. //!< ctor using all value-row-column data, plus a pointer to owner Matrix object
  705. MatVal(owner_t* own, DataType v, IndexType i, IndexType j) :
  706. owner_(own), v_(v), i_(i), j_(j) { }
  707. MatVal() = default;
  708. MatVal(const MatVal&) = delete; //!< make sure there are no copies
  709. MatVal& operator=(const MatVal&) = delete; //!< make sure there are no copies
  710. MatVal(MatVal&&) = default;
  711. MatVal& operator=(MatVal&&) = default;
  712. //! Operator to return the DataType value implicitly
  713. operator DataType() { return v_; }
  714. //! Operator to write back to owner the assigned value
  715. //! for ex: A(2,3) = 5;
  716. MatVal& operator=(DataType v) {
  717. v_ = v;
  718. owner_->set(v_, i_, j_);
  719. return *this;
  720. }
  721. private:
  722. owner_t* owner_{nullptr}; //!< Pointer to owner Matrix. MatVal is just a view.
  723. DataType v_{DataType{}}; //!< The value of the row-column pair (for speed)
  724. IndexType i_{IndexType{}}; //!< The row
  725. IndexType j_{IndexType{}}; //!< the column
  726. };
  727. } // namespace mtx
  728. #endif /* MATRIX_HPP_ */