AUTH's THMMY "Parallel and distributed systems" course assignments.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

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