520 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			520 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /**
 | |
|  * \file    impl.hpp
 | |
|  * \brief   Implementation common header file
 | |
|  *
 | |
|  * \author
 | |
|  *    Christos Choutouridis AEM:8997
 | |
|  *    <cchoutou@ece.auth.gr>
 | |
|  */
 | |
| #ifndef IMPL_HPP_
 | |
| #define IMPL_HPP_
 | |
| 
 | |
| #include <type_traits>
 | |
| #include <utility>
 | |
| #include <algorithm>
 | |
| #include <vector>
 | |
| #include <tuple>
 | |
| #include <cstddef>
 | |
| #include <iostream>
 | |
| #include <fstream>
 | |
| 
 | |
| using std::size_t;
 | |
| 
 | |
| /*
 | |
|  * Small helper to strip types
 | |
|  */
 | |
| template<typename T>
 | |
| struct remove_cvref {
 | |
|     typedef std::remove_cv_t<std::remove_reference_t<T>> type;
 | |
| };
 | |
| template<typename T>
 | |
| using remove_cvref_t = typename remove_cvref<T>::type;
 | |
| 
 | |
| /*!
 | |
|  * Enumerator to denote the storage type of the array to use.
 | |
|  */
 | |
| enum class MatrixType {
 | |
|    FULL,          /*!< Matrix is asymmetric */
 | |
|    SYMMETRIC,     /*!< Matrix is symmetric */
 | |
| };
 | |
| 
 | |
| /*
 | |
|  * Forward type declerations
 | |
|  */
 | |
| template<typename DataType, typename IndexType, MatrixType Type = MatrixType::SYMMETRIC> struct Matrix;
 | |
| template<typename DataType, typename IndexType, MatrixType Type = MatrixType::SYMMETRIC> struct SpMat;
 | |
| template<typename DataType, typename IndexType> struct SpMatCol;
 | |
| template<typename DataType, typename IndexType> struct SpMatRow;
 | |
| template<typename DataType, typename IndexType> struct SpMatVal;
 | |
| 
 | |
|  /*!
 | |
|  * 2D-array wrapper for v1 and v2 part of the exercise t use as RAII
 | |
|  * and copy-prevention.
 | |
|  *
 | |
|  * This is a very thin abstraction layer over a native array.
 | |
|  * This is tested using compiler explorer and our template produce
 | |
|  * almost identical assembly.
 | |
|  *
 | |
|  * The penalty hit we have is due to the fact that we use a one dimension array
 | |
|  * and we have to calculate the actual position from an (i,j) pair.
 | |
|  * The use of 1D array was our intention from the beginning, so the penalty
 | |
|  * was pretty much unavoidable.
 | |
|  *
 | |
|  * \tparam DataType  The underling data type of the array
 | |
|  * \tparam Type      The storage type of the array
 | |
|  *    \arg  FULL              For full matrix
 | |
|  *    \arg  SYMMETRIC         For symmetric matrix (we use only the lower part)
 | |
|  */
 | |
| template<typename DataType, typename IndexType, MatrixType Type>
 | |
| struct Matrix {
 | |
| 
 | |
|    using dataType    = DataType;                   //!< meta:export of underling data type
 | |
|    using indexType   = IndexType;                  //!< meta:export of underling index type
 | |
|    static constexpr MatrixType matrixType = Type;  //!< export of array type
 | |
| 
 | |
|    /*!
 | |
|     * \name Obj lifetime
 | |
|     */
 | |
|    //! @{
 | |
| 
 | |
|    //! Constructor using data type and size
 | |
|    Matrix(DataType* t, IndexType s) noexcept : m_(t), size_(s) { }
 | |
| 
 | |
|    //! RAII deleter
 | |
|    ~Matrix() { delete m_; }
 | |
|    //! move ctor
 | |
|    Matrix(Matrix&& a) noexcept { a.swap(*this); }
 | |
|    //! move
 | |
|    Matrix& operator=(Matrix&& a) noexcept { a.swap(*this); return *this; }
 | |
|    Matrix(const Matrix& a)             = delete;  //!< No copy ctor
 | |
|    Matrix& operator=(const Matrix& a)  = delete;  //!< No copy
 | |
|    //! @}
 | |
| 
 | |
|    //! \name Data exposure
 | |
|    //! @{
 | |
| 
 | |
|    //! memory capacity of the matrix with diagonal data
 | |
|    template<MatrixType AT=Type> std::enable_if_t<AT==MatrixType::SYMMETRIC, IndexType>
 | |
|       static constexpr capacity(IndexType N) noexcept { return (N+1)*N/2; }
 | |
|    //! memory capacity for full matrix
 | |
|    template<MatrixType AT=Type> std::enable_if_t<AT==MatrixType::FULL, IndexType>
 | |
|       static constexpr capacity(IndexType N) noexcept { return N*N; }
 | |
| 
 | |
|    //! Get the size of each dimension
 | |
|    IndexType size() noexcept { return size_; }
 | |
| 
 | |
|    /*
 | |
|     * virtual 2D accessors
 | |
|     */
 | |
|    template<MatrixType AT=Type>
 | |
|    std::enable_if_t <AT==MatrixType::SYMMETRIC, DataType>
 | |
|    get (IndexType i, IndexType j) {
 | |
|       if (i<j)    return m_[j*(j+1)/2 + i];  // Upper, use opposite index
 | |
|       else        return m_[i*(i+1)/2 + j];  // Lower, use our notation
 | |
|    }
 | |
|    template<MatrixType AT=Type>
 | |
|    std::enable_if_t <AT==MatrixType::FULL, DataType>
 | |
|    get(IndexType i, IndexType j) {
 | |
|       return m_[i*size_ + j];
 | |
|    }
 | |
|    template<MatrixType AT=Type>
 | |
|    std::enable_if_t <AT==MatrixType::SYMMETRIC, DataType>
 | |
|    set(DataType v, IndexType i, IndexType j) {
 | |
|       if (i<j)    return m_[j*(j+1)/2 + i] =v;  // Upper, use opposite index
 | |
|       else        return m_[i*(i+1)/2 + j] =v;  // Lower, use our notation
 | |
|    }
 | |
|    template<MatrixType AT=Type>
 | |
|    std::enable_if_t <AT==MatrixType::FULL, DataType>
 | |
|    set(DataType v, IndexType i, IndexType j) {
 | |
|       return m_[i*size_ + j] =v;
 | |
|    }
 | |
|    DataType operator()(IndexType i, IndexType j) { return get(i, j); }
 | |
| 
 | |
|    // a basic serial iterator support
 | |
|    DataType* begin() noexcept { return m_; }
 | |
|    DataType* end()   noexcept { return m_ + Matrix::capacity(size_); }
 | |
|    //! @}
 | |
| 
 | |
|    /*!
 | |
|     * \name Safe iteration API
 | |
|     *
 | |
|     * This api automates the iteration over the array based on
 | |
|     * MatrixType
 | |
|     */
 | |
|    //! @{
 | |
|    template<typename F, typename... Args>
 | |
|    void for_each_in (IndexType begin, IndexType end, F&& lambda, Args&&... args) {
 | |
|       for (IndexType it=begin ; it<end ; ++it) {
 | |
|          std::forward<F>(lambda)(std::forward<Args>(args)..., it);
 | |
|       }
 | |
|    }
 | |
|    //! @}
 | |
| 
 | |
|    // move helper
 | |
|    void swap(Matrix& src) noexcept {
 | |
|       std::swap(m_, src.m_);
 | |
|       std::swap(size_, src.size_);
 | |
|    }
 | |
| private:
 | |
|    DataType*   m_;      //!< Pointer to actual data.
 | |
|    IndexType   size_;   //!< the virtual size of each dimension.
 | |
| };
 | |
| 
 | |
|  /*!
 | |
|  * RAII allocation helper, the smart_ptr way.
 | |
|  */
 | |
| template<typename DataType, typename IndexType, MatrixType Type = MatrixType::SYMMETRIC>
 | |
| Matrix<DataType, IndexType, Type> make_Matrix(IndexType s) {
 | |
|    return Matrix<DataType, IndexType, Type>(new DataType[Matrix<DataType, IndexType, Type>::capacity(s)], s);
 | |
| }
 | |
| 
 | |
| template<typename DataType, typename IndexType>
 | |
| using CooVal = std::tuple<DataType, IndexType, IndexType>;
 | |
| 
 | |
| template<typename DataType, typename IndexType, MatrixType Type>
 | |
| struct SpMat {
 | |
|    using dataType    = DataType;                   //!< meta:export of underling data type
 | |
|    using indexType   = IndexType;                  //!< meta:export of underling index type
 | |
|    static constexpr MatrixType matrixType = Type;  //!< export of array type
 | |
| 
 | |
|    friend class SpMatCol<DataType, IndexType>;
 | |
|    friend class SpMatRow<DataType, IndexType>;
 | |
|    friend class SpMatVal<DataType, IndexType>;
 | |
| 
 | |
|    /*!
 | |
|     * \name Obj lifetime
 | |
|     */
 | |
|    //! @{
 | |
| 
 | |
|    //! allocation with init value ctor
 | |
|    SpMat(IndexType n=IndexType{}, IndexType nnz=IndexType{}) :
 | |
|       values(nnz, DataType{}),
 | |
|       rows(nnz, IndexType{}),
 | |
|       col_ptr((n)? n+1:2, IndexType{}),
 | |
|       N(n),
 | |
|       NNZ(nnz) { }
 | |
| 
 | |
|    SpMat(IndexType n, IndexType nnz, const IndexType* row, const IndexType* col) :
 | |
|       values(nnz, 1),
 | |
|       rows(row, row+nnz),
 | |
|       col_ptr(col, col+n+1),
 | |
|       N(n),
 | |
|       NNZ(nnz) { }
 | |
| 
 | |
|    SpMat(IndexType n, IndexType nnz, const DataType* v, const IndexType* row, const IndexType* col) :
 | |
|       values(v, v+nnz),
 | |
|       rows(row, row+nnz),
 | |
|       col_ptr(col, col+n+1),
 | |
|       N(n),
 | |
|       NNZ(nnz) { }
 | |
| 
 | |
|    SpMat(IndexType n, IndexType nnz, const DataType v, const std::vector<IndexType>& row, const std::vector<IndexType>& col) :
 | |
|       values(nnz, v),
 | |
|       rows (row),
 | |
|       col_ptr(col),
 | |
|       N(n),
 | |
|       NNZ(nnz) { }
 | |
| 
 | |
|    //! move ctor
 | |
|    SpMat(SpMat&& a) noexcept { moves(std::move(a)); }
 | |
|    //! move
 | |
|    SpMat& operator=(SpMat&& a) noexcept { moves(std::move(a)); return *this; }
 | |
|    SpMat(const SpMat& a)             = delete;  //!< No copy ctor
 | |
|    SpMat& operator=(const SpMat& a)  = delete;  //!< No copy assignment
 | |
|    //! @}
 | |
| 
 | |
|    //! \name Data exposure
 | |
|    //! @{
 | |
|    IndexType size()     noexcept { return N; }
 | |
|    IndexType size(IndexType n) {
 | |
|       col_ptr.resize(n+1);
 | |
|       return N = n;
 | |
|    }
 | |
|    IndexType capacity() noexcept { return NNZ; }
 | |
|    IndexType capacity(IndexType nnz) {
 | |
|       values.reserve(nnz);
 | |
|       rows.reserve(nnz);
 | |
|       return NNZ;
 | |
|    }
 | |
|    std::vector<IndexType>& getRows() noexcept { return rows; }
 | |
|    std::vector<IndexType>& getCols() noexcept { return col_ptr; }
 | |
| 
 | |
|    SpMatVal<DataType, IndexType> operator()(IndexType i, IndexType j) {
 | |
|       return SpMatVal<DataType, IndexType>(this, get(i, j), i, j);
 | |
|    }
 | |
|    DataType get(IndexType i, IndexType j) {
 | |
|       IndexType idx;
 | |
|       bool found;
 | |
|       std::tie(idx, found) =find_idx(rows, col_ptr[j], col_ptr[j+1], i);
 | |
|       return (found) ? values[idx] : 0;
 | |
|    }
 | |
|    DataType set(DataType v, IndexType i, IndexType j) {
 | |
|       IndexType idx; bool found;
 | |
|       std::tie(idx, found) = find_idx(rows, col_ptr[j], col_ptr[j+1], i);
 | |
|       if (found)
 | |
|          return values[idx] = v;    // we don't change NNZ even if we write "0"
 | |
|       else {
 | |
|          values.insert(values.begin()+idx, v);
 | |
|          rows.insert(rows.begin()+idx, i);
 | |
|          std::transform(col_ptr.begin()+j+1, col_ptr.end(), col_ptr.begin()+j+1, [](IndexType it) {
 | |
|             return ++it;
 | |
|          });
 | |
|          ++NNZ;                     // we increase the NNZ even if we write "0"
 | |
|          return v;
 | |
|       }
 | |
|    }
 | |
| 
 | |
| 
 | |
|    SpMatCol<DataType, IndexType> getCol(IndexType j) {
 | |
|       return SpMatCol<DataType, IndexType>(this, col_ptr[j], col_ptr[j+1]);
 | |
|    }
 | |
|    template<MatrixType AT= Type>
 | |
|    std::enable_if_t<AT==MatrixType::SYMMETRIC, SpMatCol<DataType, IndexType>>
 | |
|    getRow(IndexType i) {
 | |
|       return getCol(i);
 | |
|    }
 | |
|    template<MatrixType AT= Type>
 | |
|    std::enable_if_t<AT==MatrixType::FULL, SpMatCol<DataType, IndexType>>
 | |
|    getRow(IndexType i) {
 | |
|       return SpMatRow<DataType, IndexType>(this, i);
 | |
|    }
 | |
| 
 | |
|    // iterator support
 | |
|    DataType* begin() noexcept { return values.begin(); }
 | |
|    DataType* end()   noexcept { return values.end(); }
 | |
|    //! @}
 | |
| 
 | |
|    /*!
 | |
|     * \name Safe iteration API
 | |
|     *
 | |
|     * This api automates the iteration over the array based on
 | |
|     * MatrixType
 | |
|     */
 | |
|    //! @{
 | |
| 
 | |
|    template<typename F, typename... Args>
 | |
|    void for_each_in (IndexType begin, IndexType end, F&& lambda, Args&&... args) {
 | |
|       for (IndexType it=begin ; it<end ; ++it) {
 | |
|          std::forward<F>(lambda)(std::forward<Args>(args)..., it);
 | |
|       }
 | |
|    }
 | |
| 
 | |
|    //! @}
 | |
| 
 | |
|    // operations
 | |
|    template<typename D, typename I, MatrixType T> friend void print(SpMat<D, I, T>& mat);
 | |
|    template<typename D, typename I, MatrixType T> friend void print_dense(SpMat<D, I, T>& mat);
 | |
| 
 | |
| private:
 | |
|    // index-find helper
 | |
|    std::pair<IndexType, bool> find_idx(const std::vector<IndexType>& v, IndexType begin, IndexType end, IndexType match) {
 | |
|       for ( ; begin < end ; ++begin) {
 | |
|          if       (match == v[begin])   return std::make_pair(begin, true);
 | |
|          else if  (match <  v[begin])   return std::make_pair(begin, false);
 | |
|       }
 | |
|       return std::make_pair(end, false);
 | |
|    }
 | |
|    // move helper
 | |
|    void moves(SpMat&& src) noexcept {
 | |
|       values  = std::move(src.values);
 | |
|       rows    = std::move(src.rows);
 | |
|       col_ptr = std::move(src.col_ptr);
 | |
|       N       = std::move(src.N);   // redundant for primitives
 | |
|       NNZ     = std::move(src.NNZ); //
 | |
|    }
 | |
|    //! \name Data
 | |
|    //! @{
 | |
|    std::vector<DataType>   values {};
 | |
|    std::vector<IndexType>  rows{};
 | |
|    std::vector<IndexType>  col_ptr{1,0};
 | |
|    IndexType   N{0};
 | |
|    IndexType   NNZ{0};
 | |
|    //! @}
 | |
| };
 | |
| 
 | |
| template<typename DataType, typename IndexType>
 | |
| struct SpMatCol {
 | |
|    using owner_t = SpMat<DataType, IndexType>;
 | |
| 
 | |
|    SpMatCol(owner_t* own, const IndexType begin, const IndexType end) noexcept :
 | |
|       owner_(own), index_(begin), begin_(begin), end_(end) {
 | |
|       vindex_ = vIndexCalc(index_);
 | |
|    }
 | |
|    SpMatCol()                          = default;
 | |
|    SpMatCol(const SpMatCol&)           = delete;
 | |
|    SpMatCol& operator=(const SpMatCol&)= delete;
 | |
|    SpMatCol(SpMatCol&&)                = default;
 | |
|    SpMatCol& operator=(SpMatCol&&)     = default;
 | |
| 
 | |
|    DataType operator* () {
 | |
|       return get();
 | |
|    }
 | |
|    SpMatCol& operator++ ()    { advance(); return *this; }
 | |
|    SpMatCol& operator++ (int) { SpMatCol& p = *this; advance(); return p; }
 | |
| 
 | |
|    DataType operator()(IndexType x) {
 | |
|       return (x == index())? get() : DataType{};
 | |
|    }
 | |
|    DataType operator= (DataType v) { return owner_->values[index_] = v; }
 | |
|    IndexType       index()       noexcept { return vindex_; }
 | |
|    const IndexType index() const noexcept { return vindex_; }
 | |
|    IndexType       begin()       noexcept { return vIndexCalc(begin_); }
 | |
|    const IndexType begin() const noexcept { return vIndexCalc(begin_); }
 | |
|    IndexType       end()         noexcept { return owner_->N; }
 | |
|    const IndexType end()   const noexcept { return owner_->N; }
 | |
| 
 | |
|    template <typename C>
 | |
|    DataType operator* (C&& c) {
 | |
|       static_assert(std::is_same<remove_cvref_t<C>, SpMatCol<DataType, IndexType>>(), "");
 | |
|       DataType v{};
 | |
|       while (index() != end() && c.index() != c.end()) {
 | |
|          if      (index() < c.index())  advance();
 | |
|          else if (index() > c.index())  ++c;
 | |
|          else { //index() == c.index()
 | |
|             v += get() * *c;
 | |
|             ++c;
 | |
|             advance();
 | |
|          }
 | |
|       }
 | |
|       return v;
 | |
|    }
 | |
| 
 | |
| private:
 | |
|    void advance() noexcept {
 | |
|       ++index_;
 | |
|       vindex_ = vIndexCalc(index_);
 | |
|    }
 | |
|    IndexType vIndexCalc(IndexType idx) {
 | |
|       return (idx < end_) ? owner_->rows[idx] : end();
 | |
|    }
 | |
|    DataType get() { return owner_->values[index_]; }
 | |
|    owner_t*    owner_{nullptr};
 | |
|    IndexType   vindex_  {IndexType{}};
 | |
|    IndexType   index_{IndexType{}};
 | |
|    IndexType   begin_{IndexType{}};
 | |
|    IndexType   end_{IndexType{}};
 | |
| };
 | |
| 
 | |
| template<typename DataType, typename IndexType>
 | |
| struct SpMatRow {
 | |
|    using owner_t = SpMat<DataType, IndexType>;
 | |
| 
 | |
|    SpMatRow(owner_t* own, const IndexType row) noexcept :
 | |
|       owner_(own), vindex_(IndexType{}), row_(row), index_(IndexType{}),
 | |
|       begin_(IndexType{}), end_(owner_->NNZ) {
 | |
|       // place begin
 | |
|       while(begin_ != end_ && owner_->rows[begin_] != row_)
 | |
|          ++begin_;
 | |
|       // place index_ and vindex_
 | |
|       if (owner_->rows[index_] != row_)
 | |
|          advance();
 | |
|    }
 | |
|    SpMatRow()                          = default;
 | |
|    SpMatRow(const SpMatRow&)           = delete;
 | |
|    SpMatRow& operator=(const SpMatRow&)= delete;
 | |
|    SpMatRow(SpMatRow&&)                = default;
 | |
|    SpMatRow& operator=(SpMatRow&&)     = default;
 | |
| 
 | |
|    DataType operator* () {
 | |
|       return get();
 | |
|    }
 | |
|    SpMatRow& operator++ ()    { advance(); return *this; }
 | |
|    SpMatRow& operator++ (int) { SpMatRow& p = *this; advance(); return p; }
 | |
|    DataType operator()(IndexType x) {
 | |
|       return (x == index())? get() : DataType{};
 | |
|    }
 | |
|    DataType operator= (DataType v) { return owner_->values[index_] = v; }
 | |
|    IndexType       index()       noexcept { return vindex_; }
 | |
|    const IndexType index() const noexcept { return vindex_; }
 | |
|    IndexType       begin()       noexcept { return vIndexCalc(begin_); }
 | |
|    const IndexType begin() const noexcept { return vIndexCalc(begin_); }
 | |
|    IndexType       end()         noexcept { return owner_->N; }
 | |
|    const IndexType end()   const noexcept { return owner_->N; }
 | |
| 
 | |
|    template <typename C>
 | |
|    DataType operator* (C&& c) {
 | |
|       static_assert(std::is_same<remove_cvref_t<C>, SpMatCol<DataType, IndexType>>(), "");
 | |
|       DataType v{};
 | |
|       while (index() != end() && c.index() != c.end()) {
 | |
|          if      (index() < c.index())  advance();
 | |
|          else if (index() > c.index())  ++c;
 | |
|          else { //index() == c.index()
 | |
|             v += get()* *c;
 | |
|             ++c;
 | |
|             advance();
 | |
|          }
 | |
|       }
 | |
|       return v;
 | |
|    }
 | |
| private:
 | |
|    void advance() noexcept {
 | |
|       do
 | |
|          ++index_;
 | |
|       while(index_ != end_ && owner_->rows[index_] != row_);
 | |
|       vindex_ = vIndexCalc(index_);
 | |
|    }
 | |
|    IndexType vIndexCalc(IndexType idx) {
 | |
|       for(IndexType i =0 ; i<(owner_->N+1) ; ++i)
 | |
|          if (idx < owner_->col_ptr[i])
 | |
|             return i-1;
 | |
|       return end();
 | |
|    }
 | |
|    DataType get() { return owner_->values[index_]; }
 | |
|    owner_t*    owner_   {nullptr};
 | |
|    IndexType   vindex_  {IndexType{}};
 | |
|    IndexType   row_     {IndexType{}};
 | |
|    IndexType   index_   {IndexType{}};
 | |
|    IndexType   begin_   {IndexType{}};
 | |
|    IndexType   end_     {IndexType{}};
 | |
| };
 | |
| 
 | |
| template<typename DataType, typename IndexType>
 | |
| struct SpMatVal {
 | |
|    using owner_t = SpMat<DataType, IndexType>;
 | |
| 
 | |
|    SpMatVal(owner_t* own, DataType v, IndexType i, IndexType j) :
 | |
|       owner_(own), v_(v), i_(i), j_(j) { }
 | |
|    SpMatVal()                           = default;
 | |
|    SpMatVal(const SpMatVal&)            = delete;
 | |
|    SpMatVal& operator=(const SpMatVal&) = delete;
 | |
|    SpMatVal(SpMatVal&&)                 = default;
 | |
|    SpMatVal& operator=(SpMatVal&&)      = default;
 | |
| 
 | |
|    operator DataType() { return v_; }
 | |
|    SpMatVal& operator=(DataType v) {
 | |
|       v_ = v;
 | |
|       owner_->set(v_, i_, j_);
 | |
|       return *this;
 | |
|    }
 | |
| private:
 | |
|    owner_t*    owner_{nullptr};;
 | |
|    DataType    v_{DataType{}};
 | |
|    IndexType   i_{IndexType{}};
 | |
|    IndexType   j_{IndexType{}};
 | |
| };
 | |
| 
 | |
| 
 | |
| enum class InputMatrix{
 | |
|    GENERATE,
 | |
|    MTX
 | |
| };
 | |
| 
 | |
| /*!
 | |
|  * Session option for each invocation of the executable
 | |
|  */
 | |
| struct session_t {
 | |
|    std::size_t    size           {0};
 | |
|    double         probability    {0};
 | |
|    InputMatrix    inputMatrix    {InputMatrix::GENERATE};
 | |
|    std::ifstream  mtxFile        {};
 | |
|    std::size_t    print_size     {80};
 | |
|    bool           timing         {false};
 | |
|    bool           print          {false};
 | |
|    bool           makeSymmetric  {false};
 | |
| };
 | |
| 
 | |
| extern session_t session;
 | |
| 
 | |
| 
 | |
| #endif /* IMPL_HPP_ */
 |