AUTH's THMMY "Parallel and distributed systems" course assignments.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 
 

914 lignes
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_ */