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.
 
 
 
 
 

805 lignes
28 KiB

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