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