|
- /**
- * \file matrix.hpp
- * \brief A matrix abstraction implementation
- *
- * \author
- * Christos Choutouridis AEM:8997
- * <cchoutou@ece.auth.gr>
- */
- #ifndef MATRIX_HPP_
- #define MATRIX_HPP_
-
- #include <type_traits>
- #include <utility>
- #include <algorithm>
- #include <vector>
- #include <tuple>
-
- namespace mtx {
-
- 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 {
- DENSE, /*!< Matrix is dense */
- SPARSE, /*!< Matrix is sparse */
- };
-
- /*!
- * Enumerator to denote the storage type of the array to use.
- */
- enum class MatrixOrder {
- COLMAJOR, /*!< Matrix is column major */
- ROWMAJOR, /*!< Matrix is row major */
- };
-
- /*
- * Forward type declarations
- */
-
- template<typename MatrixType> struct MatCol;
- template<typename MatrixType> struct MatRow;
- template<typename MatrixType> struct MatVal;
-
- /*!
- * A 2-D matrix functionality over a 1-D array
- *
- * 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 IndexType The underling type for the index variables and sizes
- * \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 = size_t,
- MatrixType Type = MatrixType::DENSE,
- MatrixOrder Order = MatrixOrder::ROWMAJOR,
- bool Symmetric = false>
- struct Matrix {
-
- using dataType = DataType; //!< meta:export of underling data type
- using indexType = IndexType; //!< meta:export of underling index type
- static constexpr MatrixOrder matrixOrder = Order; //!< meta:export of array order
- static constexpr MatrixType matrixType = Type; //!< meta:export of array type
- static constexpr bool symmetric = Symmetric; //!< meta:export symmetric flag
-
- /*!
- * \name Obj lifetime
- */
- //! @{
-
- //! Construct an empty matrix with dimensions rows x columns
- Matrix(IndexType rows = IndexType{}, IndexType columns = IndexType{}) noexcept
- : vector_storage_(capacity(rows, columns)),
- raw_storage_(nullptr),
- use_vector_(true),
- rows_(rows),
- cols_(columns) {
- data_ = vector_storage_.data();
- }
-
- //! Construct a matrix by copying existing data with dimensions rows x columns
- Matrix(DataType* data, IndexType major_start, IndexType major_length, IndexType minor_length) noexcept
- : vector_storage_(),
- raw_storage_ (data + major_start * minor_length),
- use_vector_ (false) {
- if constexpr (Order == MatrixOrder::ROWMAJOR) {
- rows_ = major_length;
- cols_ = minor_length;
- }
- else {
- rows_ = minor_length;
- cols_ = major_length;
- }
- data_ = raw_storage_;
- }
-
- //! Construct a matrix using an initializer list
- Matrix(IndexType rows, IndexType columns, std::initializer_list<DataType> list)
- : vector_storage_(list),
- raw_storage_(nullptr),
- use_vector_(true),
- rows_(rows),
- cols_(columns) {
- if (list.size() != capacity(rows, columns)) {
- throw std::invalid_argument("Matrix initializer list size does not match matrix dimensions.");
- }
- data_ = vector_storage_.data();
- }
-
- //! move ctor
- Matrix(Matrix&& m) noexcept { moves(std::move(m)); }
- //! move
- Matrix& operator=(Matrix&& m) noexcept { moves(std::move(m)); return *this; }
- Matrix(const Matrix& m) = delete; //!< No copy ctor
- Matrix& operator=(const Matrix& m) = delete; //!< No copy
- //! @}
-
- //! \name Data exposure
- //! @{
-
-
- //! Get/Set the size of each dimension
- IndexType rows() const noexcept { return rows_; }
- IndexType columns() const noexcept { return cols_; }
-
- //! Get the interface size of the Matrix (what appears to be the size)
- IndexType size() const {
- return rows_ * cols_;
- }
- //! Set the interface size of the Matrix (what appears to be the size)
- IndexType resize(IndexType rows, IndexType columns) {
- if (use_vector_) {
- rows_ = rows;
- cols_ = columns;
- vector_storage_.resize(capacity(rows_, cols_));
- data_ = vector_storage_.data();
- }
- return capacity(rows_, cols_);
- }
-
- //! Actual memory capacity of the symmetric matrix
- static constexpr IndexType capacity(IndexType M, IndexType N) {
- if constexpr (Symmetric)
- return (M+1)*N/2;
- else
- return M*N;
- }
-
- /*
- * virtual 2D accessors
- */
- DataType get (IndexType i, IndexType j) {
- if constexpr (Symmetric) {
- auto T = [](size_t i)->size_t { return i*(i+1)/2; }; // Triangular number of i
- if constexpr (Order == MatrixOrder::COLMAJOR) {
- // In column major we use the lower triangle of the matrix
- if (i>=j) return data_[j*rows_ - T(j) + i]; // Lower, use our notation
- else return data_[i*rows_ - T(i) + j]; // Upper, use opposite index
- }
- else {
- // In row major we use the upper triangle of the matrix
- if (i<=j) return data_[i*cols_ - T(i) + j]; // Upper, use our notation
- else return data_[j*cols_ - T(j) + i]; // Lower, use opposite index
- }
- }
- else {
- if constexpr (Order == MatrixOrder::COLMAJOR)
- return data_[i + j*rows_];
- else
- return data_[i*cols_ + j];
- }
- }
-
- /*!
- * \fn DataType set(DataType, IndexType, IndexType)
- * \param v
- * \param i
- * \param j
- * \return
- */
- DataType set (DataType v, IndexType i, IndexType j) {
- if constexpr (Symmetric) {
- auto T = [](size_t i)->size_t { return i*(i+1)/2; }; // Triangular number of i
- if constexpr (Order == MatrixOrder::COLMAJOR) {
- // In column major we use the lower triangle of the matrix
- if (i>=j) return data_[j*rows_ - T(j) + i] = v; // Lower, use our notation
- else return data_[i*rows_ - T(i) + j] = v; // Upper, use opposite index
- }
- else {
- // In row major we use the upper triangle of the matrix
- if (i<=j) return data_[i*cols_ - T(i) + j] = v; // Upper, use our notation
- else return data_[j*cols_ - T(j) + i] = v; // Lower, use opposite index
- }
- }
- else {
- if constexpr (Order == MatrixOrder::COLMAJOR)
- return data_[i + j*rows_] = v;
- else
- return data_[i*cols_ + j] = v;
- }
- }
- // DataType operator()(IndexType i, IndexType j) { return get(i, j); }
- /*!
- * Return a proxy MatVal object with read and write capabilities.
- * @param i The row number
- * @param j The column number
- * @return tHE MatVal object
- */
- MatVal<Matrix> operator()(IndexType i, IndexType j) noexcept {
- return MatVal<Matrix>(this, get(i, j), i, j);
- }
-
- // a basic serial iterator support
- DataType* data() noexcept { return data_; }
- // IndexType begin_idx() noexcept { return 0; }
- // IndexType end_idx() noexcept { return capacity(rows_, cols_); }
-
- const DataType* data() const noexcept { return data_; }
- const IndexType begin_idx() const noexcept { return 0; }
- const IndexType end_idx() const noexcept { return capacity(rows_, cols_); }
- //! @}
-
- /*!
- * \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);
- }
- }
- //! @}
-
- //
- void swap(Matrix& src) noexcept {
- std::swap(vector_storage_, src.vector_storage_);
- std::swap(raw_storage_, src.raw_storage_);
- std::swap(data_, src.data_);
- std::swap(use_vector_, src.use_vector_);
- std::swap(rows_, src.rows_);
- std::swap(cols_, src.cols_);
- }
- private:
- //! move helper
- void moves(Matrix&& src) noexcept {
- data_ = std::move(src.vector_storage_);
- data_ = std::move(src.raw_storage_);
- data_ = std::move(src.data_);
- data_ = std::move(src.use_vector_);
- rows_ = std::move(src.rows_);
- cols_ = std::move(src.cols_);
- }
-
- std::vector<DataType>
- vector_storage_; //!< Internal storage (if used).
- DataType* raw_storage_; //!< External storage (if used).
- DataType* data_; //!< Pointer to active storage.
- bool use_vector_; //!< True if using vector storage, false for raw pointer.
- IndexType rows_{}; //!< the virtual size of rows.
- IndexType cols_{}; //!< the virtual size of columns.
- };
-
-
- /**
- * A simple sparse matrix specialization.
- *
- * We use CSC format and provide get/set functionalities for each (i,j) item
- * on the matrix. We also provide a () overload using a proxy MatVal object.
- * This way the user can:
- * \code
- * auto v = A(3,4);
- * A(3, 4) = 7;
- * \endcode
- *
- * We also provide getCol() and getRow() functions witch return a viewer/iterator to rows and
- * columns of the matrix. In the case of a symmetric matrix instead of a row we return the
- * equivalent column. This way we gain speed due to CSC format nature.
- *
- * @tparam DataType The type for values
- * @tparam IndexType The type for indexes
- * @tparam Type The Matrix type (FULL or SYMMETRIC)
- */
- template<typename DataType, typename IndexType,
- MatrixOrder Order,
- bool Symmetric>
- struct Matrix<DataType, IndexType, MatrixType::SPARSE, Order, Symmetric> {
-
- using dataType = DataType; //!< meta:export of underling data type
- using indexType = IndexType; //!< meta:export of underling index type
- static constexpr MatrixOrder matrixOrder = Order; //!< meta:export of array order
- static constexpr MatrixType matrixType = MatrixType::SPARSE; //!< meta:export of array type
- static constexpr bool symmetric = Symmetric; //!< meta:export symmetric flag
-
- friend struct MatCol<Matrix>;
- friend struct MatRow<Matrix>;
- friend struct MatVal<Matrix>;
-
- /*!
- * \name Obj lifetime
- */
- //! @{
-
- //! Default ctor with optional memory allocations
- Matrix(IndexType n=IndexType{}) noexcept:
- values{},
- rows{},
- col_ptr((n)? n+1:2, IndexType{}),
- N(n),
- NNZ(0) { }
-
- //! A ctor using csc array data
- Matrix(IndexType n, IndexType nnz, const IndexType* row, const IndexType* col) noexcept:
- values(nnz, 1),
- rows(row, row+nnz),
- col_ptr(col, col+n+1),
- N(n),
- NNZ(nnz) { }
-
- //! ctor using csc array data with value array
- Matrix(IndexType n, IndexType nnz, const DataType* v, const IndexType* row, const IndexType* col) noexcept:
- values(v, v+nnz),
- rows(row, row+nnz),
- col_ptr(col, col+n+1),
- N(n),
- NNZ(nnz) { }
-
- //! ctor vectors of row/col and default value for values array
- Matrix(IndexType n, IndexType nnz, const DataType v,
- const std::vector<IndexType>& row, const std::vector<IndexType>& col) noexcept:
- values(nnz, v),
- rows (row),
- col_ptr(col),
- N(n),
- NNZ(nnz) { }
-
- //! move ctor
- Matrix(Matrix&& m) noexcept { moves(std::move(m)); }
- //! move
- Matrix& operator=(Matrix&& m) noexcept { moves(std::move(m)); return *this; }
- Matrix(const Matrix& m) = delete; //!< make sure there are no copies
- Matrix& operator=(const Matrix& m) = delete; //!< make sure there are no copies
- //! @}
-
- //! \name Data exposure
- //! @{
-
- //! \return the dimension of the matrix
- IndexType size() noexcept { return N; }
- //! After construction size configuration tool
- IndexType resize(IndexType n) {
- col_ptr.resize(n+1);
- return N = n;
- }
- //! \return the NNZ of the matrix
- IndexType capacity() noexcept { return NNZ; }
- //! After construction NNZ size configuration tool
- IndexType capacity(IndexType nnz) noexcept {
- values.reserve(nnz);
- rows.reserve(nnz);
- return NNZ;
- }
- // getters for row arrays of the struct (unused)
- std::vector<DataType>& getValues() noexcept { return values; }
- std::vector<IndexType>& getRows() noexcept { return rows; }
- std::vector<IndexType>& getCols() noexcept { return col_ptr; }
-
- /*!
- * Return a proxy MatVal object with read and write capabilities.
- * @param i The row number
- * @param j The column number
- * @return tHE MatVal object
- */
- MatVal<Matrix> operator()(IndexType i, IndexType j) noexcept {
- return MatVal<Matrix>(this, get(i, j), i, j);
- }
-
- /*!
- * A read item functionality using binary search to find the correct row
- *
- * @param i The row number
- * @param j The column number
- * @return The value of the item or DataType{} if is not present.
- */
- DataType get(IndexType i, IndexType j) noexcept {
- IndexType idx; bool found;
- std::tie(idx, found) =find_idx(rows, col_ptr[j], col_ptr[j+1], i);
- return (found) ? values[idx] : 0;
- }
-
- /*!
- * A write item functionality.
- *
- * First we search if the matrix has already a value in (i, j) position.
- * If so we just change it to a new value. If not we add the item on the matrix.
- *
- * @note
- * When change a value, we don't increase the NNZ value of the struct. We expect the user has already
- * change the NNZ value to the right one using @see capacity() function. When adding a value we
- * increase the NNZ.
- *
- * @param i The row number
- * @param j The column number
- * @return The new value of the item .
- */
- 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;
- }
- }
-
- /*!
- * Get a view of a CSC column
- * @param j The column to get
- * @return The MatCol object @see MatCol
- */
- MatCol<Matrix> getCol(IndexType j) noexcept {
- return MatCol<Matrix>(this, col_ptr[j], col_ptr[j+1]);
- }
-
- /*!
- * Get a view of a CSC row
- *
- * In case of a SYMMETRIC matrix we can return a column instead.
- *
- * @param j The row to get
- * @return On symmetric matrix MatCol otherwise a MatRow
- */
-
- MatCol<Matrix> getRow(IndexType i) noexcept {
- if constexpr (Symmetric)
- return getCol(i);
- else
- return MatRow<Matrix>(this, i);
- }
-
- // values only iterator support
- DataType* begin() noexcept { return values.begin(); }
- DataType* end() noexcept { return values.end(); }
- //! @}
-
- //! A small iteration helper
- 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);
- }
- }
-
- private:
- /*!
- * A small binary search implementation using index for begin-end instead of iterators.
- *
- * \param v Reference to vector to search
- * \param begin The vector's index to begin
- * \param end The vector's index to end
- * \param match What to search
- * \return An <index, status> pair.
- * index is the index of the item or end if not found
- * status is true if found, false otherwise
- */
- std::pair<IndexType, bool> find_idx(const std::vector<IndexType>& v, IndexType begin, IndexType end, IndexType match) {
- if (v.capacity() != 0 && begin < end) {
- IndexType b = begin, e = end-1;
- while (b <= e) {
- IndexType m = (b+e)/2;
- if (v[m] == match) return std::make_pair(m, true);
- else if (b >= e) return std::make_pair(end, false);
- else {
- if (v[m] < match) b = m +1;
- else e = m -1;
- }
- }
- }
- return std::make_pair(end, false);
- }
-
- // move helper
- void moves(Matrix&& 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 {}; //!< vector to store the values of the matrix
- std::vector<IndexType> rows{}; //!< vector to store the row information
- std::vector<IndexType> col_ptr{1,0}; //!< vector to store the column pointers
- IndexType N{0}; //!< The dimension of the matrix (square)
- IndexType NNZ{0}; //!< The NNZ (capacity of the matrix)
- //! @}
- };
-
-
- template<typename ...> struct Matrix_view { };
-
- /*!
- * @struct Matrix_view
- * @tparam MatrixType
- */
- template<template <typename, typename, MatrixType, MatrixOrder, bool> class Matrix,
- typename DataType,
- typename IndexType,
- MatrixType Type,
- MatrixOrder Order>
- struct Matrix_view<Matrix<DataType, IndexType, Type, Order, false>> {
- using owner_t = Matrix<DataType, IndexType, Type, Order, false>;
-
- using dataType = DataType; //!< meta:export of underling data type
- using indexType = IndexType; //!< meta:export of underling index type
- static constexpr MatrixOrder matrixOrder = Order; //!< meta:export of array order
- static constexpr MatrixType matrixType = Type; //!< meta:export of array type
-
- /*!
- * \name Obj lifetime
- */
- //! @{
-
- //! Construct a matrix view to entire matrix
- Matrix_view(const owner_t* owner) noexcept :
- owner_(owner), m_(owner->data()), rows_(owner->rows()), cols_(owner->columns()) { }
-
- Matrix_view(const owner_t* owner, IndexType begin, IndexType end) noexcept :
- owner_(owner) {
- if constexpr (Order == MatrixOrder::ROWMAJOR) {
- m_ = owner->data() + begin * owner->columns();
- rows_ = end - begin;
- cols_ = owner->columns();
- } else if (Order == MatrixOrder::COLMAJOR) {
- m_ = owner->data() + begin * owner->rows();
- rows_ = owner->rows();
- cols_ = end - begin;
- }
- }
-
- Matrix_view(Matrix_view&& m) = delete; //! No move
- Matrix_view& operator=(Matrix_view&& m) = delete;
- Matrix_view(const Matrix_view& m) = delete; //!< No copy
- Matrix_view& operator=(const Matrix_view& m) = delete;
- //! @}
-
- //! Get/Set the size of each dimension
- const IndexType rows() const noexcept { return rows_; }
- const IndexType columns() const noexcept { return cols_; }
-
- //! Get the interface size of the Matrix (what appears to be the size)
- IndexType size() const {
- return rows_ * cols_;
- }
-
- //! Actual memory capacity of the symmetric matrix
- static constexpr IndexType capacity(IndexType M, IndexType N) {
- return M*N;
- }
- /*
- * virtual 2D accessors
- */
- const DataType get (IndexType i, IndexType j) const {
- if constexpr (Order == MatrixOrder::COLMAJOR)
- return m_[i + j*rows_];
- else
- return m_[i*cols_ + j];
- }
-
- DataType set (DataType v, IndexType i, IndexType j) {
- if constexpr (Order == MatrixOrder::COLMAJOR)
- return m_[i + j*rows_] = v;
- else
- return m_[i*cols_ + j] = v;
- }
- // DataType operator()(IndexType i, IndexType j) { return get(i, j); }
- /*!
- * Return a proxy MatVal object with read and write capabilities.
- * @param i The row number
- * @param j The column number
- * @return tHE MatVal object
- */
- MatVal<Matrix_view> operator()(IndexType i, IndexType j) noexcept {
- return MatVal<Matrix_view>(this, get(i, j), i, j);
- }
-
- // a basic serial iterator support
- DataType* data() noexcept { return m_.data(); }
- // IndexType begin_idx() noexcept { return 0; }
- // IndexType end_idx() noexcept { return capacity(rows_, cols_); }
-
- const DataType* data() const noexcept { return m_; }
- const IndexType begin_idx() const noexcept { return 0; }
- const IndexType end_idx() const noexcept { return capacity(rows_, cols_); }
- //! @}
-
- /*!
- * \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);
- }
- }
- //! @}
- //!
- private:
- const owner_t* owner_ {nullptr}; //!< Pointer to Matrix
- DataType* m_ {nullptr}; //!< Starting address of the slice/view
- IndexType rows_{}; //!< the virtual size of rows.
- IndexType cols_{}; //!< the virtual size of columns.
- };
-
- /*!
- * A view/iterator hybrid object for Matrix columns.
- *
- * This object provides access to a column of a Matrix. The public functionalities
- * allow data access using indexes instead of iterators. We prefer indexes over iterators
- * because we can apply the same index to different inner vector of Matrix without conversion.
- *
- * @tparam DataType
- * @tparam IndexType
- */
- template<typename MatrixType>
- struct MatCol {
- using owner_t = MatrixType;
-
- using DataType = typename MatrixType::dataType;
- using IndexType = typename MatrixType::indexType;
-
- /*!
- * ctor using column pointers for begin-end. own is pointer to Matrix.
- */
- MatCol(owner_t* own, const IndexType begin, const IndexType end) noexcept :
- owner_(own), index_(begin), begin_(begin), end_(end) {
- vindex_ = vIndexCalc(index_);
- }
- MatCol() = default;
- MatCol(const MatCol&) = delete; //!< make sure there are no copies
- MatCol& operator=(const MatCol&)= delete; //!< make sure there are no copies
- MatCol(MatCol&&) = default;
- MatCol& operator=(MatCol&&) = default;
-
- //! a simple dereference operator, like an iterator
- DataType operator* () {
- return get();
- }
- //! Increment operator acts on index(), like an iterator
- MatCol& operator++ () { advance(); return *this; }
- MatCol& operator++ (int) { MatCol& p = *this; advance(); return p; }
-
- //! () operator acts as member access (like a view)
- DataType operator()(IndexType x) {
- return (x == index())? get() : DataType{};
- }
- //! = operator acts as member assignment (like a view)
- DataType operator= (DataType v) { return owner_->values[index_] = v; }
- // iterator like handlers
- // these return a virtual index value based on the items position on the full matrix
- // but the move of the index is just a ++ away.
- 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; }
-
- /*!
- * Multiplication operator
- *
- * We follow only the non-zero values and multiply only the common indexes.
- *
- * @tparam C Universal reference for the type right half site column
- *
- * @param c The right hand site matrix
- * @return The value of the inner product of two vectors
- * @note The time complexity is \$ O(nnz1+nnz2) \$.
- * Where the nnz is the max NNZ elements of the column of the matrix
- */
- template <typename C>
- DataType operator* (C&& c) {
- static_assert(std::is_same<remove_cvref_t<C>, MatCol<MatrixType>>(), "");
- DataType v{};
- while (index() != end() && c.index() != c.end()) {
- if (index() < c.index()) advance(); // advance me
- else if (index() > c.index()) ++c; // advance other
- else { //index() == c.index()
- v += get() * *c; // multiply and advance both
- ++c;
- advance();
- }
- }
- return v;
- }
-
- private:
- //! small tool to increase the index pointers to Matrix
- void advance() noexcept {
- ++index_;
- vindex_ = vIndexCalc(index_);
- }
- //! tool to translate between col_ptr indexes and Matrix "virtual" full matrix indexes
- IndexType vIndexCalc(IndexType idx) {
- return (idx < end_) ? owner_->rows[idx] : end();
- }
- //! small get tool
- DataType get() { return owner_->values[index_]; }
-
- owner_t* owner_ {nullptr}; //!< Pointer to owner Matrix. MatCol is just a view
- IndexType vindex_ {IndexType{}}; //!< Virtual index of full matrix
- IndexType index_ {IndexType{}}; //!< index to Matrix::rows
- IndexType begin_ {IndexType{}}; //!< beginning index of the column in Matrix::rows
- IndexType end_ {IndexType{}}; //!< ending index of the column in Matrix::rows
- };
-
- /*!
- * A view/iterator hybrid object for Matrix rows.
- *
- * This object provides access to a column of a Matrix. The public functionalities
- * allow data access using indexes instead of iterators. We prefer indexes over iterators
- * because we can apply the same index to different inner vector of Matrix without conversion.
- *
- * @tparam DataType
- * @tparam IndexType
- */
- template<typename MatrixType>
- struct MatRow {
- using owner_t = MatrixType;
-
- using DataType = typename MatrixType::dataType;
- using IndexType = typename MatrixType::indexType;
-
- /*!
- * ctor using virtual full matrix row index. own is pointer to Matrix.
- */
- MatRow(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();
- }
- MatRow() = default;
- MatRow(const MatRow&) = delete; //!< make sure there are no copies
- MatRow& operator=(const MatRow&)= delete; //!< make sure there are no copies
- MatRow(MatRow&&) = default;
- MatRow& operator=(MatRow&&) = default;
-
- //! a simple dereference operator, like an iterator
- DataType operator* () {
- return get();
- }
- //! Increment operator acts on index(), like an iterator
- //! here the increment is a O(N) process.
- MatRow& operator++ () { advance(); return *this; }
- MatRow& operator++ (int) { MatRow& p = *this; advance(); return p; }
-
- //! () operator acts as member access (like a view)
- DataType operator()(IndexType x) {
- return (x == index())? get() : DataType{};
- }
- //! = operator acts as member assignment (like a view)
- DataType operator= (DataType v) { return owner_->values[index_] = v; }
- // iterator like handlers
- // these return a virtual index value based on the items position on the full matrix
- // but the move of the index is just a ++ away.
- 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; }
-
- /*!
- * Multiplication operator
- *
- * We follow only the non-zero values and multiply only the common indexes.
- *
- * @tparam C Universal reference for the type right half site column
- *
- * @param c The right hand site matrix
- * @return The value of the inner product of two vectors
- * @note The time complexity is \$ O(N+nnz2) \$ and way heavier the ColxCol multiplication.
- * Where the nnz is the max NNZ elements of the column of the matrix
- */
- template <typename C>
- DataType operator* (C&& c) {
- static_assert(std::is_same<remove_cvref_t<C>, MatCol<MatrixType>>(), "");
- DataType v{};
- while (index() != end() && c.index() != c.end()) {
- if (index() < c.index()) advance(); // advance me
- else if (index() > c.index()) ++c; // advance other
- else { //index() == c.index()
- v += get() * *c; // multiply and advance both
- ++c;
- advance();
- }
- }
- return v;
- }
- private:
- //! small tool to increase the index pointers to Matrix matrix
- //! We have to search the entire rows vector in Matrix to find the next
- //! virtual row position.
- //! time complexity O(N)
- void advance() noexcept {
- do
- ++index_;
- while(index_ != end_ && owner_->rows[index_] != row_);
- vindex_ = vIndexCalc(index_);
- }
- //! tool to translate between col_ptr indexes and Matrix "virtual" full matrix indexes
- IndexType vIndexCalc(IndexType idx) {
- for(IndexType i =0 ; i<(owner_->N+1) ; ++i)
- if (idx < owner_->col_ptr[i])
- return i-1;
- return end();
- }
- //! small get tool
- DataType get() { return owner_->values[index_]; }
-
- owner_t* owner_ {nullptr}; //!< Pointer to owner Matrix. MatCol is just a view
- IndexType vindex_ {IndexType{}}; //!< Virtual index of full matrix
- IndexType row_ {IndexType{}}; //!< The virtual full matrix row of the object
- IndexType index_ {IndexType{}}; //!< index to Matrix::rows
- IndexType begin_ {IndexType{}}; //!< beginning index of the column in Matrix::rows
- IndexType end_ {IndexType{}}; //!< ending index of the column in Matrix::rows
- };
-
- /*!
- * A proxy Matrix value object/view.
- *
- * This object acts as proxy to provide read/write access to an Matrix item.
- *
- * @tparam DataType The type of the values of the Matrix matrix
- * @tparam IndexType The type of the indexes of the Matrix matrix
- */
- template<typename MatrixType>
- struct MatVal {
- using owner_t = MatrixType;
-
- using DataType = typename MatrixType::dataType;
- using IndexType = typename MatrixType::indexType;
-
- //!< ctor using all value-row-column data, plus a pointer to owner Matrix object
- MatVal(owner_t* own, DataType v, IndexType i, IndexType j) :
- owner_(own), v_(v), i_(i), j_(j) { }
- MatVal() = default;
- MatVal(const MatVal&) = delete; //!< make sure there are no copies
- MatVal& operator=(const MatVal&) = delete; //!< make sure there are no copies
- MatVal(MatVal&&) = default;
- MatVal& operator=(MatVal&&) = default;
-
- //! Operator to return the DataType value implicitly
- operator DataType() { return v_; }
- //! Operator to write back to owner the assigned value
- //! for ex: A(2,3) = 5;
- MatVal& operator=(DataType v) {
- v_ = v;
- owner_->set(v_, i_, j_);
- return *this;
- }
- private:
- owner_t* owner_{nullptr}; //!< Pointer to owner Matrix. MatVal is just a view.
- DataType v_{DataType{}}; //!< The value of the row-column pair (for speed)
- IndexType i_{IndexType{}}; //!< The row
- IndexType j_{IndexType{}}; //!< the column
- };
-
-
- } // namespace mtx
-
-
- #endif /* MATRIX_HPP_ */
|