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.
 
 
 
 
 

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