A triangle counting assignment for A.U.TH Parallel and distributed systems class.
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.
 
 
 
 
 
 

520 lines
17 KiB

  1. /**
  2. * \file impl.hpp
  3. * \brief Implementation common header file
  4. *
  5. * \author
  6. * Christos Choutouridis AEM:8997
  7. * <cchoutou@ece.auth.gr>
  8. */
  9. #ifndef IMPL_HPP_
  10. #define IMPL_HPP_
  11. #include <type_traits>
  12. #include <utility>
  13. #include <algorithm>
  14. #include <vector>
  15. #include <tuple>
  16. #include <cstddef>
  17. #include <iostream>
  18. #include <fstream>
  19. using std::size_t;
  20. /*
  21. * Small helper to strip types
  22. */
  23. template<typename T>
  24. struct remove_cvref {
  25. typedef std::remove_cv_t<std::remove_reference_t<T>> type;
  26. };
  27. template<typename T>
  28. using remove_cvref_t = typename remove_cvref<T>::type;
  29. /*!
  30. * Enumerator to denote the storage type of the array to use.
  31. */
  32. enum class MatrixType {
  33. FULL, /*!< Matrix is asymmetric */
  34. SYMMETRIC, /*!< Matrix is symmetric */
  35. };
  36. /*
  37. * Forward type declerations
  38. */
  39. template<typename DataType, typename IndexType, MatrixType Type = MatrixType::SYMMETRIC> struct Matrix;
  40. template<typename DataType, typename IndexType, MatrixType Type = MatrixType::SYMMETRIC> struct SpMat;
  41. template<typename DataType, typename IndexType> struct SpMatCol;
  42. template<typename DataType, typename IndexType> struct SpMatRow;
  43. template<typename DataType, typename IndexType> struct SpMatVal;
  44. /*!
  45. * 2D-array wrapper for v1 and v2 part of the exercise t use as RAII
  46. * and copy-prevention.
  47. *
  48. * This is a very thin abstraction layer over a native array.
  49. * This is tested using compiler explorer and our template produce
  50. * almost identical assembly.
  51. *
  52. * The penalty hit we have is due to the fact that we use a one dimension array
  53. * and we have to calculate the actual position from an (i,j) pair.
  54. * The use of 1D array was our intention from the beginning, so the penalty
  55. * was pretty much unavoidable.
  56. *
  57. * \tparam DataType The underling data type of the array
  58. * \tparam Type The storage type of the array
  59. * \arg FULL For full matrix
  60. * \arg SYMMETRIC For symmetric matrix (we use only the lower part)
  61. */
  62. template<typename DataType, typename IndexType, MatrixType Type>
  63. struct Matrix {
  64. using dataType = DataType; //!< meta:export of underling data type
  65. using indexType = IndexType; //!< meta:export of underling index type
  66. static constexpr MatrixType matrixType = Type; //!< export of array type
  67. /*!
  68. * \name Obj lifetime
  69. */
  70. //! @{
  71. //! Constructor using data type and size
  72. Matrix(DataType* t, IndexType s) noexcept : m_(t), size_(s) { }
  73. //! RAII deleter
  74. ~Matrix() { delete m_; }
  75. //! move ctor
  76. Matrix(Matrix&& a) noexcept { a.swap(*this); }
  77. //! move
  78. Matrix& operator=(Matrix&& a) noexcept { a.swap(*this); return *this; }
  79. Matrix(const Matrix& a) = delete; //!< No copy ctor
  80. Matrix& operator=(const Matrix& a) = delete; //!< No copy
  81. //! @}
  82. //! \name Data exposure
  83. //! @{
  84. //! memory capacity of the matrix with diagonal data
  85. template<MatrixType AT=Type> std::enable_if_t<AT==MatrixType::SYMMETRIC, IndexType>
  86. static constexpr capacity(IndexType N) noexcept { return (N+1)*N/2; }
  87. //! memory capacity for full matrix
  88. template<MatrixType AT=Type> std::enable_if_t<AT==MatrixType::FULL, IndexType>
  89. static constexpr capacity(IndexType N) noexcept { return N*N; }
  90. //! Get the size of each dimension
  91. IndexType size() noexcept { return size_; }
  92. /*
  93. * virtual 2D accessors
  94. */
  95. template<MatrixType AT=Type>
  96. std::enable_if_t <AT==MatrixType::SYMMETRIC, DataType>
  97. get (IndexType i, IndexType j) {
  98. if (i<j) return m_[j*(j+1)/2 + i]; // Upper, use opposite index
  99. else return m_[i*(i+1)/2 + j]; // Lower, use our notation
  100. }
  101. template<MatrixType AT=Type>
  102. std::enable_if_t <AT==MatrixType::FULL, DataType>
  103. get(IndexType i, IndexType j) {
  104. return m_[i*size_ + j];
  105. }
  106. template<MatrixType AT=Type>
  107. std::enable_if_t <AT==MatrixType::SYMMETRIC, DataType>
  108. set(DataType v, IndexType i, IndexType j) {
  109. if (i<j) return m_[j*(j+1)/2 + i] =v; // Upper, use opposite index
  110. else return m_[i*(i+1)/2 + j] =v; // Lower, use our notation
  111. }
  112. template<MatrixType AT=Type>
  113. std::enable_if_t <AT==MatrixType::FULL, DataType>
  114. set(DataType v, IndexType i, IndexType j) {
  115. return m_[i*size_ + j] =v;
  116. }
  117. DataType operator()(IndexType i, IndexType j) { return get(i, j); }
  118. // a basic serial iterator support
  119. DataType* begin() noexcept { return m_; }
  120. DataType* end() noexcept { return m_ + Matrix::capacity(size_); }
  121. //! @}
  122. /*!
  123. * \name Safe iteration API
  124. *
  125. * This api automates the iteration over the array based on
  126. * MatrixType
  127. */
  128. //! @{
  129. template<typename F, typename... Args>
  130. void for_each_in (IndexType begin, IndexType end, F&& lambda, Args&&... args) {
  131. for (IndexType it=begin ; it<end ; ++it) {
  132. std::forward<F>(lambda)(std::forward<Args>(args)..., it);
  133. }
  134. }
  135. //! @}
  136. // move helper
  137. void swap(Matrix& src) noexcept {
  138. std::swap(m_, src.m_);
  139. std::swap(size_, src.size_);
  140. }
  141. private:
  142. DataType* m_; //!< Pointer to actual data.
  143. IndexType size_; //!< the virtual size of each dimension.
  144. };
  145. /*!
  146. * RAII allocation helper, the smart_ptr way.
  147. */
  148. template<typename DataType, typename IndexType, MatrixType Type = MatrixType::SYMMETRIC>
  149. Matrix<DataType, IndexType, Type> make_Matrix(IndexType s) {
  150. return Matrix<DataType, IndexType, Type>(new DataType[Matrix<DataType, IndexType, Type>::capacity(s)], s);
  151. }
  152. template<typename DataType, typename IndexType>
  153. using CooVal = std::tuple<DataType, IndexType, IndexType>;
  154. template<typename DataType, typename IndexType, MatrixType Type>
  155. struct SpMat {
  156. using dataType = DataType; //!< meta:export of underling data type
  157. using indexType = IndexType; //!< meta:export of underling index type
  158. static constexpr MatrixType matrixType = Type; //!< export of array type
  159. friend class SpMatCol<DataType, IndexType>;
  160. friend class SpMatRow<DataType, IndexType>;
  161. friend class SpMatVal<DataType, IndexType>;
  162. /*!
  163. * \name Obj lifetime
  164. */
  165. //! @{
  166. //! allocation with init value ctor
  167. SpMat(IndexType n=IndexType{}, IndexType nnz=IndexType{}) :
  168. values(nnz, DataType{}),
  169. rows(nnz, IndexType{}),
  170. col_ptr((n)? n+1:2, IndexType{}),
  171. N(n),
  172. NNZ(nnz) { }
  173. SpMat(IndexType n, IndexType nnz, const IndexType* row, const IndexType* col) :
  174. values(nnz, 1),
  175. rows(row, row+nnz),
  176. col_ptr(col, col+n+1),
  177. N(n),
  178. NNZ(nnz) { }
  179. SpMat(IndexType n, IndexType nnz, const DataType* v, const IndexType* row, const IndexType* col) :
  180. values(v, v+nnz),
  181. rows(row, row+nnz),
  182. col_ptr(col, col+n+1),
  183. N(n),
  184. NNZ(nnz) { }
  185. SpMat(IndexType n, IndexType nnz, const DataType v, const std::vector<IndexType>& row, const std::vector<IndexType>& col) :
  186. values(nnz, v),
  187. rows (row),
  188. col_ptr(col),
  189. N(n),
  190. NNZ(nnz) { }
  191. //! move ctor
  192. SpMat(SpMat&& a) noexcept { moves(std::move(a)); }
  193. //! move
  194. SpMat& operator=(SpMat&& a) noexcept { moves(std::move(a)); return *this; }
  195. SpMat(const SpMat& a) = delete; //!< No copy ctor
  196. SpMat& operator=(const SpMat& a) = delete; //!< No copy assignment
  197. //! @}
  198. //! \name Data exposure
  199. //! @{
  200. IndexType size() noexcept { return N; }
  201. IndexType size(IndexType n) {
  202. col_ptr.resize(n+1);
  203. return N = n;
  204. }
  205. IndexType capacity() noexcept { return NNZ; }
  206. IndexType capacity(IndexType nnz) {
  207. values.reserve(nnz);
  208. rows.reserve(nnz);
  209. return NNZ;
  210. }
  211. std::vector<IndexType>& getRows() noexcept { return rows; }
  212. std::vector<IndexType>& getCols() noexcept { return col_ptr; }
  213. SpMatVal<DataType, IndexType> operator()(IndexType i, IndexType j) {
  214. return SpMatVal<DataType, IndexType>(this, get(i, j), i, j);
  215. }
  216. DataType get(IndexType i, IndexType j) {
  217. IndexType idx;
  218. bool found;
  219. std::tie(idx, found) =find_idx(rows, col_ptr[j], col_ptr[j+1], i);
  220. return (found) ? values[idx] : 0;
  221. }
  222. DataType set(DataType v, IndexType i, IndexType j) {
  223. IndexType idx; bool found;
  224. std::tie(idx, found) = find_idx(rows, col_ptr[j], col_ptr[j+1], i);
  225. if (found)
  226. return values[idx] = v; // we don't change NNZ even if we write "0"
  227. else {
  228. values.insert(values.begin()+idx, v);
  229. rows.insert(rows.begin()+idx, i);
  230. std::transform(col_ptr.begin()+j+1, col_ptr.end(), col_ptr.begin()+j+1, [](IndexType it) {
  231. return ++it;
  232. });
  233. ++NNZ; // we increase the NNZ even if we write "0"
  234. return v;
  235. }
  236. }
  237. SpMatCol<DataType, IndexType> getCol(IndexType j) {
  238. return SpMatCol<DataType, IndexType>(this, col_ptr[j], col_ptr[j+1]);
  239. }
  240. template<MatrixType AT= Type>
  241. std::enable_if_t<AT==MatrixType::SYMMETRIC, SpMatCol<DataType, IndexType>>
  242. getRow(IndexType i) {
  243. return getCol(i);
  244. }
  245. template<MatrixType AT= Type>
  246. std::enable_if_t<AT==MatrixType::FULL, SpMatCol<DataType, IndexType>>
  247. getRow(IndexType i) {
  248. return SpMatRow<DataType, IndexType>(this, i);
  249. }
  250. // iterator support
  251. DataType* begin() noexcept { return values.begin(); }
  252. DataType* end() noexcept { return values.end(); }
  253. //! @}
  254. /*!
  255. * \name Safe iteration API
  256. *
  257. * This api automates the iteration over the array based on
  258. * MatrixType
  259. */
  260. //! @{
  261. template<typename F, typename... Args>
  262. void for_each_in (IndexType begin, IndexType end, F&& lambda, Args&&... args) {
  263. for (IndexType it=begin ; it<end ; ++it) {
  264. std::forward<F>(lambda)(std::forward<Args>(args)..., it);
  265. }
  266. }
  267. //! @}
  268. // operations
  269. template<typename D, typename I, MatrixType T> friend void print(SpMat<D, I, T>& mat);
  270. template<typename D, typename I, MatrixType T> friend void print_dense(SpMat<D, I, T>& mat);
  271. private:
  272. // index-find helper
  273. std::pair<IndexType, bool> find_idx(const std::vector<IndexType>& v, IndexType begin, IndexType end, IndexType match) {
  274. for ( ; begin < end ; ++begin) {
  275. if (match == v[begin]) return std::make_pair(begin, true);
  276. else if (match < v[begin]) return std::make_pair(begin, false);
  277. }
  278. return std::make_pair(end, false);
  279. }
  280. // move helper
  281. void moves(SpMat&& src) noexcept {
  282. values = std::move(src.values);
  283. rows = std::move(src.rows);
  284. col_ptr = std::move(src.col_ptr);
  285. N = std::move(src.N); // redundant for primitives
  286. NNZ = std::move(src.NNZ); //
  287. }
  288. //! \name Data
  289. //! @{
  290. std::vector<DataType> values {};
  291. std::vector<IndexType> rows{};
  292. std::vector<IndexType> col_ptr{1,0};
  293. IndexType N{0};
  294. IndexType NNZ{0};
  295. //! @}
  296. };
  297. template<typename DataType, typename IndexType>
  298. struct SpMatCol {
  299. using owner_t = SpMat<DataType, IndexType>;
  300. SpMatCol(owner_t* own, const IndexType begin, const IndexType end) noexcept :
  301. owner_(own), index_(begin), begin_(begin), end_(end) {
  302. vindex_ = vIndexCalc(index_);
  303. }
  304. SpMatCol() = default;
  305. SpMatCol(const SpMatCol&) = delete;
  306. SpMatCol& operator=(const SpMatCol&)= delete;
  307. SpMatCol(SpMatCol&&) = default;
  308. SpMatCol& operator=(SpMatCol&&) = default;
  309. DataType operator* () {
  310. return get();
  311. }
  312. SpMatCol& operator++ () { advance(); return *this; }
  313. SpMatCol& operator++ (int) { SpMatCol& p = *this; advance(); return p; }
  314. DataType operator()(IndexType x) {
  315. return (x == index())? get() : DataType{};
  316. }
  317. DataType operator= (DataType v) { return owner_->values[index_] = v; }
  318. IndexType index() noexcept { return vindex_; }
  319. const IndexType index() const noexcept { return vindex_; }
  320. IndexType begin() noexcept { return vIndexCalc(begin_); }
  321. const IndexType begin() const noexcept { return vIndexCalc(begin_); }
  322. IndexType end() noexcept { return owner_->N; }
  323. const IndexType end() const noexcept { return owner_->N; }
  324. template <typename C>
  325. DataType operator* (C&& c) {
  326. static_assert(std::is_same<remove_cvref_t<C>, SpMatCol<DataType, IndexType>>(), "");
  327. DataType v{};
  328. while (index() != end() && c.index() != c.end()) {
  329. if (index() < c.index()) advance();
  330. else if (index() > c.index()) ++c;
  331. else { //index() == c.index()
  332. v += get() * *c;
  333. ++c;
  334. advance();
  335. }
  336. }
  337. return v;
  338. }
  339. private:
  340. void advance() noexcept {
  341. ++index_;
  342. vindex_ = vIndexCalc(index_);
  343. }
  344. IndexType vIndexCalc(IndexType idx) {
  345. return (idx < end_) ? owner_->rows[idx] : end();
  346. }
  347. DataType get() { return owner_->values[index_]; }
  348. owner_t* owner_{nullptr};
  349. IndexType vindex_ {IndexType{}};
  350. IndexType index_{IndexType{}};
  351. IndexType begin_{IndexType{}};
  352. IndexType end_{IndexType{}};
  353. };
  354. template<typename DataType, typename IndexType>
  355. struct SpMatRow {
  356. using owner_t = SpMat<DataType, IndexType>;
  357. SpMatRow(owner_t* own, const IndexType row) noexcept :
  358. owner_(own), vindex_(IndexType{}), row_(row), index_(IndexType{}),
  359. begin_(IndexType{}), end_(owner_->NNZ) {
  360. // place begin
  361. while(begin_ != end_ && owner_->rows[begin_] != row_)
  362. ++begin_;
  363. // place index_ and vindex_
  364. if (owner_->rows[index_] != row_)
  365. advance();
  366. }
  367. SpMatRow() = default;
  368. SpMatRow(const SpMatRow&) = delete;
  369. SpMatRow& operator=(const SpMatRow&)= delete;
  370. SpMatRow(SpMatRow&&) = default;
  371. SpMatRow& operator=(SpMatRow&&) = default;
  372. DataType operator* () {
  373. return get();
  374. }
  375. SpMatRow& operator++ () { advance(); return *this; }
  376. SpMatRow& operator++ (int) { SpMatRow& p = *this; advance(); return p; }
  377. DataType operator()(IndexType x) {
  378. return (x == index())? get() : DataType{};
  379. }
  380. DataType operator= (DataType v) { return owner_->values[index_] = v; }
  381. IndexType index() noexcept { return vindex_; }
  382. const IndexType index() const noexcept { return vindex_; }
  383. IndexType begin() noexcept { return vIndexCalc(begin_); }
  384. const IndexType begin() const noexcept { return vIndexCalc(begin_); }
  385. IndexType end() noexcept { return owner_->N; }
  386. const IndexType end() const noexcept { return owner_->N; }
  387. template <typename C>
  388. DataType operator* (C&& c) {
  389. static_assert(std::is_same<remove_cvref_t<C>, SpMatCol<DataType, IndexType>>(), "");
  390. DataType v{};
  391. while (index() != end() && c.index() != c.end()) {
  392. if (index() < c.index()) advance();
  393. else if (index() > c.index()) ++c;
  394. else { //index() == c.index()
  395. v += get()* *c;
  396. ++c;
  397. advance();
  398. }
  399. }
  400. return v;
  401. }
  402. private:
  403. void advance() noexcept {
  404. do
  405. ++index_;
  406. while(index_ != end_ && owner_->rows[index_] != row_);
  407. vindex_ = vIndexCalc(index_);
  408. }
  409. IndexType vIndexCalc(IndexType idx) {
  410. for(IndexType i =0 ; i<(owner_->N+1) ; ++i)
  411. if (idx < owner_->col_ptr[i])
  412. return i-1;
  413. return end();
  414. }
  415. DataType get() { return owner_->values[index_]; }
  416. owner_t* owner_ {nullptr};
  417. IndexType vindex_ {IndexType{}};
  418. IndexType row_ {IndexType{}};
  419. IndexType index_ {IndexType{}};
  420. IndexType begin_ {IndexType{}};
  421. IndexType end_ {IndexType{}};
  422. };
  423. template<typename DataType, typename IndexType>
  424. struct SpMatVal {
  425. using owner_t = SpMat<DataType, IndexType>;
  426. SpMatVal(owner_t* own, DataType v, IndexType i, IndexType j) :
  427. owner_(own), v_(v), i_(i), j_(j) { }
  428. SpMatVal() = default;
  429. SpMatVal(const SpMatVal&) = delete;
  430. SpMatVal& operator=(const SpMatVal&) = delete;
  431. SpMatVal(SpMatVal&&) = default;
  432. SpMatVal& operator=(SpMatVal&&) = default;
  433. operator DataType() { return v_; }
  434. SpMatVal& operator=(DataType v) {
  435. v_ = v;
  436. owner_->set(v_, i_, j_);
  437. return *this;
  438. }
  439. private:
  440. owner_t* owner_{nullptr};;
  441. DataType v_{DataType{}};
  442. IndexType i_{IndexType{}};
  443. IndexType j_{IndexType{}};
  444. };
  445. enum class InputMatrix{
  446. GENERATE,
  447. MTX
  448. };
  449. /*!
  450. * Session option for each invocation of the executable
  451. */
  452. struct session_t {
  453. std::size_t size {0};
  454. double probability {0};
  455. InputMatrix inputMatrix {InputMatrix::GENERATE};
  456. std::ifstream mtxFile {};
  457. std::size_t print_size {80};
  458. bool timing {false};
  459. bool print {false};
  460. bool makeSymmetric {false};
  461. };
  462. extern session_t session;
  463. #endif /* IMPL_HPP_ */