sparse matrix implemented using vector of map - c++

As I wrote in another post (closed because I found some solution) I have implement a Sparse Matrix Class , the data container is done by a vactor of map where the index of vector rappresent the index of the row of the matrix for each index therefore the vector stored a map !
here the interface:
template <typename element_type>
class SparseMatrix {
public:
template<class T>
friend SparseMatrix<T> operator+(const SparseMatrix<T>& m1 , const SparseMatrix<T>& m2 );
template<class T>
friend SparseMatrix<T> operator-(const SparseMatrix<T>& m1 , const SparseMatrix<T>& m2 );
template <class T>
friend SparseMatrix<T> operator*(const SparseMatrix<T>& m1 , const SparseMatrix<T>& m2 );
public:
// container type ;
using data_type = std::vector<std::map<std::size_t , element_type >> ;
using it_rows = typename std::map<std::size_t , element_type>::iterator ;
SparseMatrix(std::size_t rows , std::size_t cols) : rows{rows} , columns{cols}
{
data.resize(rows);
}
SparseMatrix(std::initializer_list<std::initializer_list<element_type>> l );
SparseMatrix(const std::string );
auto insert(std::size_t i , std::pair<std::size_t, element_type> p )
{
assert( i <= rows && p.first <= columns); // , "Index out of bound" );
data.at(i).insert(p);
}
auto insert(std::size_t i, std::size_t j, element_type val)
{
assert(i<=rows && j <=columns);
data.at(i)[j] = val ;
}
auto identity() noexcept ;
auto diag(const element_type& v) noexcept ;
auto print() const noexcept ;
auto dataType() const noexcept ;
auto traspose() noexcept ;
private:
std::size_t rows ;
std::size_t columns ;
data_type data ; // vector containing row element
};
In the previous post I asked about the traspose matrix .. I found this solution:
template <typename T>
auto SparseMatrix<T>::traspose() noexcept
{
//rows = columns ;
std::swap(rows,columns);
data_type newData ;
newData.resize(rows);
for(std::size_t i=0; i < columns ; i++)
{
for(std::size_t j=0 ; j < rows ; j++)
{
if(data.at(i).find(j) != data.at(i).end())
{
newData.at(j)[i] = data.at(i).at(j) ;
}
}
}
std::swap(data,newData);
}
Now i have no idea to how do compute the matrix product ... could you give me some advice ?
Ok .. thanks to suggeriment recived by #BeyelerStudios i got the poind and wrote the dot product in this way :
template <class T>
SparseMatrix<T> dot(const SparseMatrix<T>& m1 , const SparseMatrix<T>& m2 )
{
if(m1.columns != m2.rows)
{
throw std::runtime_error("Matrix sizes dosent match the dot-product");
}
SparseMatrix<T> result{m1.rows , m2.columns };
for(std::size_t i=0 ; i < result.rows ; i++)
{
for(std::size_t j=0 ; j < result.columns ; j++ )
{
for(std::size_t k=0; k< m1.columns ; k++)
{
if( m1.data.at(i).find(k) != m1.data.at(i).end() &&
m2.data.at(k).find(j) != m2.data.at(k).end() )
{
result.data.at(i)[j] += (m1.data.at(i).at(k) * m2.data.at(k).at(j)) ;
}
}
}
}
return result ;
}
now i open one other post for asking about determinant and inverse !

Related

c++ how to correctly overload + operator

I need to implement a matrix class for my study, and now I'm stuck with operator + overloading.
Here's my matrix.cpp code:
template<class type>
matrix<type> matrix<type>::operator+ (const matrix<type>& matObj){
matrix<type> newMat(this->WIDTH, this->HEIGHT, 0);
for (int i = 0; i < this->HEIGHT; i++)
for (int j = 0; j < this->WIDTH; j++)
newMat.array[WIDTH * i + j] = matObj.array[WIDTH * i + j] + this->array[WIDTH * i + j];
return newMat;
}
And here's main.cpp code:
int main() {
vector< vector<int>> v = {
{1, 2, 3},
{4, 5, 6},
{1, 2, 9}
};
math::matrix<int> mat1(v), mat2;
mat2 = mat1+mat1;
for (int i = 0; i < mat2.cols(); i++) {
for (int j = 0; j < mat2.rows(); j++) {
cout << mat2[i][j] << ", ";
}
cout << endl;
}
cin.get();
return 0;
}
Assume that all constructors and other codes are written correctly.
So the problem is when I try to run the program, it fails on line:
mat2 = mat1+mat1;
And when I try to print newMat in matrix.cpp before the return statement, it prints it correctly. And of course, I have implemented = operator which works fine. Any suggestions?
The easy way to implement + is as follows.
struct example {
int state = 0;
example& operator+=( example const& rhs )& {
// increment state here; change in real code
state += rhs.state;
return *this;
}
friend example operator+( example lhs, example const& rhs ) {
lhs += rhs;
return std::move(lhs);
}
example( example&& ) = default; // ensure this is efficient
example& operator=( example&& ) = default; // ensure this is efficient
example& operator=( example const& ) = default; // this can be expensive
example( example const& ) = default; // this can be expensive
};
more concretely
template<class T>
struct matrix {
std::vector< std::vector<T> > state;
matrix & operator+=( matrix const& rhs )& {
// increment state here; change in real code
for (std::size_t i = 0; i < (std::min)(state.size(), rhs.state.size(); ++i) {
for (std::size_t j = 0; j < (std::min)(state[i].size(), rhs.state[i].size(); ++j) {
state[i][j] += rhs.state[i][j];
}
}
return *this;
}
friend matrix operator+( matrix lhs, matrix const& rhs ) {
lhs += rhs;
return std::move(lhs);
}
matrix( matrix&& ) = default; // ensure this is efficient
matrix& operator=( matrix&& ) = default; // ensure this is efficient
matrix& operator=( matrix const& ) = default; // this can be expensive
matrix( matrix const& ) = default; // this can be expensive
}
=default works on matrix(matrix&&) etc because I followed the rule of 0 -- I left data storage to a specialized class (std::vector<?>), and it has efficient move semantics, so I get it automatically.
If you swap state for a std::vector< T > that has width*height elements, you just have to adapt how you access it. (That is more efficient, but I was trying to keep it simple).

find a value in a block matrix

I wrote a sparse matrix class, based on Block compressed storage, I wrote almost all the method, but I have not idea to how to write the method findValue(i,j) that give 2 indexes of the original matrix ! the storage consists in four vectors :
`ba_': stored the non zero block (rectangular block in which almost one element is different from zero) of the matrix in top-down left-right order
an_ is the vector of index that points to the first element of the block in the vector ba
aj_ stored the index of the block columns in the blocked matrix.
ai_ stored the first block of each row in the blocked matrix.
the picture clarify anything :
here the following class in which I use two methods to achieve the result, findBlockIndex and findValue(i,j,Brows,Bcols) but I need to get the value of the original i,j index using findValue(i,j) in which i,j are the index in the sparse complete matrix
# include <iosfwd>
# include <vector>
# include <string>
# include <initializer_list>
# include "MatrixException.H"
# include <sstream>
# include <fstream>
# include <algorithm>
# include <iomanip>
// forward declarations
template <typename T, std::size_t R, std::size_t C>
class BCRSmatrix ;
template <typename T, std::size_t R, std::size_t C>
std::ostream& operator<<(std::ostream& os , const BCRSmatrix<T,R,C>& m );
template <typename T, std::size_t Br, std::size_t Bc >
std::vector<T> operator*(const BCRSmatrix<T,Br,Bc>& m, const std::vector<T>& x );
template <typename data_type, std::size_t BR , std::size_t BC>
class BCRSmatrix {
template <typename T, std::size_t R, std::size_t C>
friend std::ostream& operator<<(std::ostream& os , const BCRSmatrix<T,R,C>& m );
template <typename T, std::size_t Br,std::size_t Bc>
friend std::vector<T> operator*(const BCRSmatrix<T,Br,Bc>& m, const std::vector<T>& x );
public:
constexpr BCRSmatrix(std::initializer_list<std::vector<data_type>> dense );
constexpr BCRSmatrix(const std::string& );
virtual ~BCRSmatrix() = default ;
auto constexpr print_block(const std::vector<std::vector<data_type>>& dense,
std::size_t i, std::size_t j) const noexcept ;
auto constexpr validate_block(const std::vector<std::vector<data_type>>& dense,
std::size_t i, std::size_t j) const noexcept ;
auto constexpr insert_block(const std::vector<std::vector<data_type>>& dense,
std::size_t i, std::size_t j) noexcept ;
auto constexpr printBCRS() const noexcept ;
auto constexpr printBlockMatrix() const noexcept ;
auto constexpr size1() const noexcept { return denseRows ;}
auto constexpr size2() const noexcept { return denseCols ;}
auto constexpr printBlock(std::size_t i) const noexcept ;
auto constexpr print() const noexcept ;
private:
std::size_t bn ;
std::size_t bBR ;
std::size_t nnz ;
std::size_t denseRows ;
std::size_t denseCols ;
std::vector<data_type> ba_ ;
std::vector<std::size_t> an_ ;
std::vector<std::size_t> ai_ ;
std::vector<std::size_t> aj_ ;
std::size_t index =0 ;
auto constexpr findBlockIndex(const std::size_t r, const std::size_t c) const noexcept ;
auto constexpr recomposeMatrix() const noexcept ;
auto constexpr findValue(
const std::size_t i, const std::size_t j,
const std::size_t rBlock, const std::size_t cBlock
) const noexcept ;
};
//--------------------------- IMPLEMENTATION
template <typename T, std::size_t BR, std::size_t BC>
constexpr BCRSmatrix<T,BR,BC>::BCRSmatrix(std::initializer_list<std::vector<T>> dense_ )
{
this->denseRows = dense_.size();
auto it = *(dense_.begin());
this->denseCols = it.size();
if( (denseRows*denseCols) % BR != 0 )
{
throw InvalidSizeException("Error block size is not multiple of dense matrix size");
}
std::vector<std::vector<T>> dense(dense_);
bBR = BR*BC ;
bn = denseRows*denseCols/(BR*BC) ;
ai_.resize(denseRows/BR +1);
ai_[0] = 1;
for(std::size_t i = 0; i < dense.size() / BR ; i++)
{
auto rowCount =0;
for(std::size_t j = 0; j < dense[i].size() / BC ; j++)
{
if(validate_block(dense,i,j))
{
aj_.push_back(j+1);
insert_block(dense, i, j);
rowCount ++ ;
}
}
ai_[i+1] = ai_[i] + rowCount ;
}
printBCRS();
}
template <typename T, std::size_t BR, std::size_t BC>
constexpr BCRSmatrix<T,BR,BC>::BCRSmatrix(const std::string& fname)
{
std::ifstream f(fname , std::ios::in);
if(!f)
{
throw OpeningFileException("error opening file in constructor !");
}
else
{
std::vector<std::vector<T>> dense;
std::string line, tmp;
T elem = 0 ;
std::vector<T> row;
std::size_t i=0, j=0 ;
while(getline(f, line))
{
row.clear();
std::istringstream ss(line);
if(i==0)
{
while(ss >> elem)
{
row.push_back(elem);
j++;
}
}
else
{
while(ss >> elem)
row.push_back(elem);
}
dense.push_back(row);
i++;
}
this->denseRows = i;
this->denseCols = j;
bBR = BR*BR ;
bn = denseRows*denseCols/(BR*BC) ;
ai_.resize(denseRows/BR +1);
ai_[0] = 1;
for(std::size_t i = 0; i < dense.size() / BR ; i++)
{
auto rowCount =0;
for(std::size_t j = 0; j < dense[i].size() / BC ; j++)
{
if(validate_block(dense,i,j))
{
aj_.push_back(j+1);
insert_block(dense, i, j);
rowCount ++ ;
}
}
ai_[i+1] = ai_[i] + rowCount ;
}
}
printBCRS();
}
template <typename T,std::size_t BR, std::size_t BC>
inline auto constexpr BCRSmatrix<T,BR,BC>::printBlockMatrix() const noexcept
{
for(auto i=0 ; i < denseRows / BR ; i++)
{
for(auto j=1 ; j <= denseCols / BC ; j++)
{
std::cout << findBlockIndex(i,j) << ' ' ;
}
std::cout << std::endl;
}
}
template <typename T,std::size_t BR,std::size_t BC>
inline auto constexpr BCRSmatrix<T,BR,BC>::printBlock(std::size_t i) const noexcept
{
auto w = i-1 ;
auto k = 0;
for(std::size_t i = 0 ; i < BR ; ++i)
{
for(std::size_t j=0 ; j < BC ; ++j )
{
std::cout << std::setw(8) << ba_.at(an_.at(w)-1+k) << ' ';
k++;
}
}
}
template <typename T,std::size_t BR, std::size_t BC>
inline auto constexpr BCRSmatrix<T,BR,BC>::print_block(const std::vector<std::vector<T>>& dense,
std::size_t i, std::size_t j) const noexcept
{
for(std::size_t m = i * BR ; m < BR * (i + 1); ++m)
{
for(std::size_t n = j * BC ; n < BC * (j + 1); ++n)
std::cout << dense[m][n] << ' ';
std::cout << '\n';
}
}
template <typename T,std::size_t BR, std::size_t BC>
inline auto constexpr BCRSmatrix<T,BR,BC>::validate_block(const std::vector<std::vector<T>>& dense,
std::size_t i, std::size_t j) const noexcept
{
bool nonzero = false ;
for(std::size_t m = i * BR ; m < BR * (i + 1); ++m)
{
for(std::size_t n = j * BC ; n < BC * (j + 1); ++n)
{
if(dense[m][n] != 0) nonzero = true;
}
}
return nonzero ;
}
template <typename T,std::size_t BR, std::size_t BC>
inline auto constexpr BCRSmatrix<T,BR,BC>::insert_block(const std::vector<std::vector<T>>& dense,
std::size_t i, std::size_t j) noexcept
{
bool firstElem = true ;
for(std::size_t m = i * BR ; m < BR * (i + 1); ++m)
{
for(std::size_t n = j * BC ; n < BC * (j + 1); ++n)
{
if(firstElem)
{
an_.push_back(index+1);
firstElem = false ;
}
ba_.push_back(dense[m][n]);
index ++ ;
}
}
}
template <typename T, std::size_t BR,std::size_t BC>
auto constexpr BCRSmatrix<T,BR,BC>::findBlockIndex(const std::size_t r, const std::size_t c) const noexcept
{
for(auto j= ai_.at(r) ; j < ai_.at(r+1) ; j++ )
{
if( aj_.at(j-1) == c )
{
return j ;
}
}
}
template <typename T, std::size_t BR, std::size_t BC>
auto constexpr BCRSmatrix<T,BR,BC>::printBCRS() const noexcept
{
std::cout << "ba_ : " ;
for(auto &x : ba_ )
std::cout << x << ' ' ;
std::cout << std::endl;
std::cout << "an_ : " ;
for(auto &x : an_ )
std::cout << x << ' ' ;
std::cout << std::endl;
std::cout << "aj_ : " ;
for(auto &x : aj_ )
std::cout << x << ' ' ;
std::cout << std::endl;
std::cout << "ai_ : " ;
for(auto &x : ai_ )
std::cout << x << ' ' ;
std::cout << std::endl;
}
template <typename T, std::size_t BR, std::size_t BC>
auto constexpr BCRSmatrix<T,BR,BC>::print() const noexcept
{
//for each BCRS row
for(auto i=0 ; i < denseRows / BR ; i++){
//for each Block sub row.
for(auto rBlock = 0; rBlock < BR; rBlock++){
//for each BCSR col.
for(auto j = 1; j <= denseCols / BC; j++){
//for each Block sub col.
for(auto cBlock = 0; cBlock < BC; cBlock++){
std::cout<< findValue(i, j, rBlock, cBlock) <<'\t';
}
}
std::cout << std::endl;
}
}
}
template <typename T, std::size_t BR,std::size_t BC>
auto constexpr BCRSmatrix<T,BR,BC>::recomposeMatrix() const noexcept
{
std::vector<std::vector<T>> sparseMat(denseRows, std::vector<T>(denseCols, 0));
auto BA_i = 0, AJ_i = 0;
//for each BCSR row
for(auto r = 0; r < denseRows/BR; r++){
//for each Block in row
for(auto nBlock = 0; nBlock < ai_.at(r+1)-ai_.at(r); nBlock++){
//for each subMatrix (Block)
for(auto rBlock = 0; rBlock < BR; rBlock++){
for(auto cBlock = 0; cBlock < BC; cBlock++){
//insert value
sparseMat.at(rBlock + r*BR).at(cBlock + (aj_.at(AJ_i)-1)*BC) = ba_.at(BA_i);
++BA_i;
}
}
++AJ_i;
}
}
return sparseMat;
}
template <typename T, std::size_t BR,std::size_t BC>
auto constexpr BCRSmatrix<T,BR,BC>::findValue(
const std::size_t i, const std::size_t j,
const std::size_t rBlock, const std::size_t cBlock
) const noexcept
{
auto index = findBlockIndex(i,j);
if(index != 0)
return ba_.at(an_.at(index-1)-1 + cBlock + rBlock*BC);
else
return T(0);
}
template <typename T, std::size_t BR,std::size_t BC>
std::ostream& operator<<(std::ostream& os , const BCRSmatrix<T,BR,BC>& m )
{
for(auto i=0 ; i < m.denseRows / BR ; i++)
{
//for each Block sub row.
for(auto rBlock = 0; rBlock < BR; rBlock++)
{
//for each BCSR col.
for(auto j = 1; j <= m.denseCols / BC; j++)
{
//for each Block sub col.
for(auto cBlock = 0; cBlock < BC; cBlock++)
{
os << m.findValue(i, j, rBlock, cBlock) <<'\t';
}
}
os << std::endl;
}
}
return os;
}
template <typename T, std::size_t BR, std::size_t BC>
std::vector<T> operator*(const BCRSmatrix<T,BR,BC>& m, const std::vector<T>& x )
{
std::vector<T> y(x.size());
if(m.size1() != x.size())
{
std::string to = "x" ;
std::string mess = "Error occured in operator* attempt to perfor productor between op1: "
+ std::to_string(m.size1()) + to + std::to_string(m.size2()) +
" and op2: " + std::to_string(x.size());
throw InvalidSizeException(mess.c_str());
}
else
{
auto brows = m.denseRows/BR ;
auto bnze = m.an_.size() ;
auto z=0;
for(auto b=0 ; b < brows ; b++)
{
for(auto j= m.ai_.at(b) ; j <= m.ai_.at(b+1)-1; j++ )
{
for(auto k=0 ; k < BR ; k++ )
{
for(auto t=0 ; t < BC ; t++)
{
y.at(BC*b+k) += m.ba_.at(z) * x.at(BC*(m.aj_.at(j-1)-1)+t) ;
z++ ;
}
}
}
}
}
return y;
}
and this is the main
# include "BCSmatrix.H"
using namespace std;
int main(){
BCRSmatrix<int,2,2> bbcsr1 = {{11,12,13,14,0,0},{0,22,23,0,0,0},{0,0,33,34,35,36},{0,0,0,44,45,0},
{0,0,0,0,0,56},{0,0,0,0,0,66}};
BCRSmatrix<int,2,2> bbcsr2 = {{11,12,0,0,0,0,0,0} ,{0,22,0,0,0,0,0,0} ,{31,32,33,0,0,0,0,0},
{41,42,43,44,0,0,0,0}, {0,0,0,0,55,56,0,0},{0,0,0,0,0,66,67,0},{0,0,0,0,0,0,77,78},{0,0,0,0,0,0,87,88}};
BCRSmatrix<int,2,4> bbcsr3 = {{11,12,0,0,0,0,0,0} ,{0,22,0,0,0,0,0,0} ,{31,32,33,0,0,0,0,0},
{41,42,43,44,0,0,0,0}, {0,0,0,0,55,56,0,0},{0,0,0,0,0,66,67,0},{0,0,0,0,0,0,77,78},{0,0,0,0,0,0,87,88}};
bbcsr3.printBlockMatrix();
bbcsr3.print();
BCRSmatrix<int,2,2> bbcsr4("input17.dat");
bbcsr4.printBlockMatrix();
BCRSmatrix<int,2,4> bbcsr5("input18.dat");
bbcsr5.printBlockMatrix();
cout << bbcsr5 ;
BCRSmatrix<int,4,4> bbcsr6("input18.dat");
bbcsr6.printBlockMatrix();
bbcsr6.print();
cout << bbcsr4 ; //.print();
BCRSmatrix<int,2,4> bbcsr7("input20.dat");
cout << bbcsr7;
bbcsr7.printBlockMatrix();
std::vector<int> v1 = {3,4,0,1,6,8,1,19};
std::vector<int> v01 = {3,4,0,1,6,8,1,19,15,2};
std::vector<int> v2 = bbcsr4 *v1 ;
for(auto& x : v2)
cout << x << ' ' ;
cout << endl;
BCRSmatrix<double,2,2> bbcsr8("input21.dat");
bbcsr8.print() ;
bbcsr8.printBlockMatrix();
return 0;
}
how to write the method findValue(i,j) that give 2 indexes of the original matrix
It is similar to the previous findValue method:
template <typename T, std::size_t BR,std::size_t BC>
auto constexpr BCRSmatrix<T,BR,BC>::myNewfindValue(const std::size_t i, const std::size_t j) const noexcept{
auto index = findBlockIndex(i/BR, j/BC);
if(index != 0)
return ba_.at(an_.at(index-1)-1 + j%BC + (i%BR)*BC);
else
return T(0);
}
To recall this function: you have to do a little change to your findBlockIndex: just change if( aj_.at(j-1) == c ) whit if( aj_.at(j-1) == c+1 ), than you have to modify your for statements in the others functions for(auto j = 1; j <= .. whit for(auto j = 0; j < ...
Let me know if there are problems or this is not the answer you were looking for.
I hope to be of help to you,
best regards Marco.
rename the original findValue as findVal then define a new findValue that take exactly 2 element defined as follow (I know is orrible):
template <typename T, std::size_t BS>
T constexpr SqBCSmatrix<T,BS>::findValue(const std::size_t r, const std::size_t c) const noexcept
{
//for each BCRS row
for(auto i=0 ,k=0; i < denseRows / BS ; i++){
//for each Block sub row.
for(auto rBlock = 0; rBlock < BS; k++ ,rBlock++){
//for each BCSR col.
for(auto j = 1 , l=0; j <= denseCols / BS; j++){
//for each Block sub col.
for(auto cBlock = 0; cBlock < BS; l++ , cBlock++){
if(k == r && c == l )
return findVal(i,j,rBlock, cBlock);
}
}
}
}
return 0;
}

how to convert block compressed row to dense matrix?

I'm interesting to create a class for storing sparse matrix in Block Compressed Sparse Row format this method of storage consist to subdivide the matrix into square block of size sz*sz and stored this block in a vector BA , here you can find most information about link
basically the matrix is stored using 4 vector :
BA contains the elements of the submatrices (blocks) stored in top-down left right order (the first block in the picture of size 2x2 is 11,12,0,22)
AN contains the indices of each starting block of the vector BA (in the pictur case the block size is 2x2 so it contains 1,5 ... )
AJ contains the column index of blocks in the matrix of blocks (the smaller one in the picture)
AI the row pointer vector , it store how many blocks there is in the i-th row ai[i+1]-a[i] = number of block in i-th row
I'm write the constructor for convert a matrix from dense format to BCRS format :
template <typename data_type, std::size_t SZ = 2 >
class BCSRmatrix {
public:
constexpr BCSRmatrix(std::initializer_list<std::vector<data_type>> dense );
auto constexpr validate_block(const std::vector<std::vector<data_type>>& dense,
std::size_t i, std::size_t j) const noexcept ;
auto constexpr insert_block(const std::vector<std::vector<data_type>>& dense,
std::size_t i, std::size_t j) noexcept ;
private:
std::size_t bn ;
std::size_t bSZ ;
std::size_t nnz ;
std::size_t denseRows ;
std::size_t denseCols ;
std::vector<data_type> ba_ ;
std::vector<std::size_t> an_ ;
std::vector<std::size_t> ai_ ;
std::vector<std::size_t> aj_ ;
std::size_t index =0 ;
};
template <typename T, std::size_t SZ>
constexpr BCSRmatrix<T,SZ>::BCSRmatrix(std::initializer_list<std::vector<T>> dense_ )
{
this->denseRows = dense_.size();
auto it = *(dense_.begin());
this->denseCols = it.size();
if( (denseRows*denseCols) % SZ != 0 )
{
throw InvalidSizeException("Error block size is not multiple of dense matrix size");
}
std::vector<std::vector<T>> dense(dense_);
bSZ = SZ*SZ ;
bn = denseRows*denseCols/(SZ*SZ) ;
ai_.resize(denseRows/SZ +1);
ai_[0] = 1;
for(std::size_t i = 0; i < dense.size() / SZ ; i++)
{
auto rowCount =0;
for(std::size_t j = 0; j < dense[i].size() / SZ ; j++)
{
if(validate_block(dense,i,j))
{
aj_.push_back(j+1);
insert_block(dense, i, j);
rowCount ++ ;
}
}
ai_[i+1] = ai_[i] + rowCount ;
}
printBCSR();
}
template <typename T,std::size_t SZ>
inline auto constexpr BCSRmatrix<T,SZ>::validate_block(const std::vector<std::vector<T>>& dense,
std::size_t i, std::size_t j) const noexcept
{
bool nonzero = false ;
for(std::size_t m = i * SZ ; m < SZ * (i + 1); ++m)
{
for(std::size_t n = j * SZ ; n < SZ * (j + 1); ++n)
{
if(dense[m][n] != 0) nonzero = true;
}
}
return nonzero ;
}
template <typename T,std::size_t SZ>
inline auto constexpr BCSRmatrix<T,SZ>::insert_block(const std::vector<std::vector<T>>& dense,
std::size_t i, std::size_t j) noexcept
{
//std::size_t value = index;
bool firstElem = true ;
for(std::size_t m = i * SZ ; m < SZ * (i + 1); ++m)
{
for(std::size_t n = j * SZ ; n < SZ * (j + 1); ++n)
{
if(firstElem)
{
an_.push_back(index+1);
firstElem = false ;
}
ba_.push_back(dense[m][n]);
index ++ ;
}
}
template <typename T, std::size_t SZ>
auto constexpr BCSRmatrix<T,SZ>::printBCSR() const noexcept
{
std::cout << "ba_ : " ;
for(auto &x : ba_ )
std::cout << x << ' ' ;
std::cout << std::endl;
std::cout << "an_ : " ;
for(auto &x : an_ )
std::cout << x << ' ' ;
std::cout << std::endl;
std::cout << "aj_ : " ;
for(auto &x : aj_ )
std::cout << x << ' ' ;
std::cout << std::endl;
std::cout << "ai_ : " ;
for(auto &x : ai_ )
std::cout << x << ' ' ;
std::cout << std::endl;
}
And the main function for test the class :
# include "BCSRmatrix.H"
using namespace std;
int main(){
BCSRmatrix<int,2> bbcsr2 = {{11,12,0,0,0,0,0,0} ,{0,22,0,0,0,0,0,0} ,{31,32,33,0,0,0,0,0},
{41,42,43,44,0,0,0,0}, {0,0,0,0,55,56,0,0},{0,0,0,0,0,66,67,0},{0,0,0,0,0,0,77,78},{0,0,0,0,0,0,87,88}};
BCSRmatrix<int,4> bbcsr3 = {{11,12,0,0,0,0,0,0} ,{0,22,0,0,0,0,0,0} ,{31,32,33,0,0,0,0,0},
{41,42,43,44,0,0,0,0}, {0,0,0,0,55,56,0,0},{0,0,0,0,0,66,67,0},{0,0,0,0,0,0,77,78},{0,0,0,0,0,0,87,88}};
return 0;
}
Now back to the question .. I obtain the 4 vector as in the picture .. but what about backing from this 4 vector to the dense matrix ?
for example how to print out the whole matrix ?
Edit : I've figure out the way to plot the "blocks matrix" the smaller in the picture with relative index of vector AN:
template <typename T,std::size_t SZ>
inline auto constexpr BCSRmatrix<T,SZ>::printBlockMatrix() const noexcept
{
for(auto i=0 ; i < denseRows / SZ ; i++)
{
for(auto j=1 ; j <= denseCols / SZ ; j++)
{
std::cout << findBlockIndex(i,j) << ' ' ;
}
std::cout << std::endl;
}
}
template <typename T, std::size_t SZ>
auto constexpr BCSRmatrix<T,SZ>::findBlockIndex(const std::size_t r, const std::size_t c) const noexcept
{
for(auto j= ai_.at(r) ; j < ai_.at(r+1) ; j++ )
{
if( aj_.at(j-1) == c )
{
return j ;
}
}
}
that when in the main I call :
bbcsr3.printBlockMatrix();
Give me the right result :
1 0 0 0
2 3 0 0
0 0 4 5
0 0 0 6
Now just the whole matrix missing I think that I missed something in may mind .. but should be something easy but I didn't got the point .. any ideas ?
what about backing from this 4 vector to the dense matrix ? for example how to print out the whole matrix ?
Back to the sparse matrix:
template <typename T, std::size_t SZ>
auto constexpr BCSRmatrix<T,SZ>::recomposeMatrix() const noexcept {
std::vector<std::vector<data_type>> sparseMat(denseRows, std::vector<data_type>(denseCols, 0));
auto BA_i = 0, AJ_i = 0;
//for each BCSR row
for(auto r = 0; r < denseRows/SZ; r++){
//for each Block in row
for(auto nBlock = 0; nBlock < ai_.at(r+1)-ai_.at(r); nBlock++){
//for each subMatrix (Block)
for(auto rBlock = 0; rBlock < SZ; rBlock++){
for(auto cBlock = 0; cBlock < SZ; cBlock++){
//insert value
sparseMat.at(rBlock + r*SZ).at(cBlock + (aj_.at(AJ_i)-1)*SZ) = ba_.at(BA_i);
++BA_i;
}
}
++AJ_i;
}
}
return sparseMat;
}
Where:
BA_i and AJ_i are iterators of the respective vectors.
nBlock keeps the numbers of blocks in row given by ai_.
rBlock and cBlockare the iterators of the sub-matrix sz*sz called "Block".
note: an_ remain unused, you can try replacing BA_i whit it.
Print the matrix:
std::vector<std::vector<int>> sparseMat = bbcsr2.recomposeMatrix();
for(auto i = 0; i < sparseMat.size(); i++){
for(auto j = 0; j < sparseMat.at(i).size(); j++)
std::cout<<sparseMat.at(i).at(j) << '\t';
std::cout << std::endl;
}
I'm not sure I wrote the template correctly, anyway the algorithm should work; let me know if there are problems.
EDIT
make sense in a class that is created for saving time and memory storing sparse matrix it certain way than use a vector for reconstruct the whole matrix ?
You're right, my fault; I thought the problem was recompose the Matrix.
I rewritten the methods using findBlockIndex as a reference.
template <typename T, std::size_t SZ>
auto constexpr BCSRmatrix<T,SZ>::printSparseMatrix() const noexcept {
//for each BCSR row
for(auto i=0 ; i < denseRows / SZ ; i++){
//for each Block sub row.
for(auto rBlock = 0; rBlock < SZ; rBlock++){
//for each BCSR col.
for(auto j = 1; j <= denseCols / SZ; j++){
//for each Block sub col.
for(auto cBlock = 0; cBlock < SZ; cBlock++){
std::cout<< findValue(i, j, rBlock, cBlock) <<'\t';
}
}
std::cout << std::endl;
}
}
}
template <typename T, std::size_t SZ>
auto constexpr BCSRmatrix<T,SZ>::findValue(const std::size_t i, const std::size_t j, const std::size_t rBlock, const std::size_t cBlock) const noexcept {
auto index = findBlockIndex(i,j);
if(index != 0)
return ba_.at(an_.at(index-1)-1 + cBlock + /* rBlock*2 */ rBlock*SZ);
}
I hope to be of help to you,
best regards Marco.

Matrix (MCSC format ) product how to perform?

I'm implementing a matrix class in modified compressed sparse column format , I have not idea to how to perform the product , this matrix store all the non zero element in 2 vector (value and index) in particular this format of storing consist of 2 container construct in this way:
aa_ the vector of value, stored in is first matrix-dim element the value of the diagonal, and then all the non zero value off-diagonal
ja_ stored in it's first matrix-dim element the number of non zero off-diagonal element in this way :ja[0]=matrix.dimension +1 then ja_[i] -ja_[i+1] = nnz element of column i+1 , and in the ja[ja_[i]] = index_of_row of the non zero element.
If you would read something about this format you can look here Modified compressed format
I've implemented a class but I would figure out how to perform the matrix product, I hope somebody can help me about
# include <iosfwd>
# include <initializer_list>
# include <iomanip>
# include <cassert>
# include <cmath>
# include <vector>
template <typename data_type> class MCSCmatrix ;
template <typename T>
std::vector<T> operator*(const MCSCmatrix<T>& A ,const std::vector<T>& x)noexcept ;
template <typename data_type>
class MCSCmatrix {
public:
template <typename T>
friend std::vector<T> operator*(const MCSCmatrix<T>& A ,const std::vector<T>& x) noexcept ;
auto constexpr printMCSC() const noexcept ;
private:
std::vector<data_type> aa_ ; // non zero value value
std::vector<std::size_t> ja_ ;
std::size_t dim ;
};
template <typename T>
inline constexpr MCSCmatrix<T>::MCSCmatrix( std::initializer_list<std::vector<T>> row)
{
this->dim = row.size();
auto il = *(row.begin());
if(this-> dim != il.size())
{
throw InvalidSizeException("Matrix Must be square in Modified CSC format ");
}
std::vector<std::vector<T>> temp(row);
aa_.resize(dim+1);
ja_.resize(dim+1);
//std::size_t elemCount = 0;
ja_[0] = dim+2 ;
auto elemCount = 0;
for(auto c = 0 ; c < temp[0].size() ; c++ )
{
elemCount =0 ;
for(auto r = 0 ; r < temp.size() ; r++)
{
if(c==r)
{
aa_[c] = temp[r][c] ;
}
else if(c != r && temp[r][c] !=0)
{
aa_.push_back(temp[r][c]);
ja_.push_back(r+1);
elemCount++ ;
}
}
ja_[c+1] = ja_[c] + elemCount ;
}
printMCSC();
}
template <typename T>
inline auto constexpr MCSCmatrix<T>::printMCSC() const noexcept
{
std::cout << "aa: " ;
for(auto& x : aa_ )
std::cout << x << ' ' ;
std::cout << std::endl;
std::cout << "ja: " ;
for(auto& x : ja_ )
std::cout << x << ' ' ;
std::cout << std::endl;
}
template <typename T>
std::vector<T> operator*(const MCSCmatrix<T>& A ,const std::vector<T>& x) noexcept
{
assert(A.dim == x.size());
std::vector<T> b(x.size());
for(auto i=0 ; i < A.dim ; i++ )
b.at(i) = A.aa_.at(i) * x.at(i) ; // diagonal value
for(auto i=0; i< A.dim ; i++)
{
for(auto k=A.ja_.at(i)-1 ; k < A.ja_.at(i+1)-1 ; k++ )
{
b.at(A.ja_.at(k)-1) += A.aa_.at(k)* x.at(i);
}
}
return b;
}
and here the main function:
# include "ModCSCmatrix.H"
using namespace std;
int main(){
MCSCmatrix<int> m1 = {{11,12,13,14,0,0},{0,22,23,0,0,0},{0,0,33,34,35,36},{0,0,0,44,45,0},
{0,0,0,0,0,56},{0,0,0,0,0,66}};
m1.printMCSC();
MCSCmatrix<double> m100 = {{1.01, 0 , 2.34,0}, {0, 4.07, 0,0},{3.12,0,6.08,0},{1.06,0,2.2,9.9} };
std::vector<double> v1={0,1.3,4.2,0.8};
std::vector<double> v2 = m100*v1 ;
for(auto& x : v2)
cout << x << ' ' ;
cout << endl;
return 0;
}

Matrix Mod. sparse row Operator*(matrix , vector)

I'm implementing a modified compressed sparse row matrix [reference],
but I have a problem with Matrix * vector multiplication, I wrote the function but I don't reach to find the bug !
the class used 2 container (std::vector) for store
Diagonal element (aa_[0] to aa_[dim])
the non zero value off-diagonal (aa_[dim+2] to aa_[size_of_non_zero])
pointer of the first element in the row (ja_[0] to ja_[dim] )
in the previous pointer this rules is used : ja_[0]=dim+1 ; ja_[i+1]-ja[i]= number of element in i-th row
column index stored in ja_[ja_[row]] for ja_[row] described above is range is ja[0] to ja[dim+1] ,so the colum index are in ja_[dim+2] to ja_[size_of_non_zero elment]
here the minimal code :
# include <initializer_list>
# include <vector>
# include <iosfwd>
# include <string>
# include <cstdlib>
# include <cassert>
# include <iomanip>
# include <cmath> for(auto i=0; i< A.dim ; i++)
{
//for(auto k=A.ja_.at(i) ; k <= A.ja_.at(i+1)-1 ; k++ )
auto k=A.ja_.at(i)-1;
do
{
b.at(i) += A.aa_.at(k)* x.at(A.ja_.at(k)-1);
k++ ; for(auto i=0; i< A.dim ; i++)
{
//for(auto k=A.ja_.at(i) ; k <= A.ja_.at(i+1)-1 ; k++ )
auto k=A.ja_.at(i)-1;
do
{
b.at(i) += A.aa_.at(k)* x.at(A.ja_.at(k)-1);
k++ ;
}while (k < A.ja_.at(i+1)-1 ); // ;
}
return b;
}while (k < A.ja_.at(i+1)-1 ); // ;
}
return b;
# include <set>
# include <fstream>
template <typename data_type>
class MCSRmatrix {
public:
using itype = std::size_t ;
template <typename T>
friend std::vector<T> operator*(const MCSRmatrix<T>& A, const std::vector<T>& x ) noexcept ;
public:
constexpr MCSRmatrix( std::initializer_list<std::initializer_list<data_type>> rows);
private:
std::vector<data_type> aa_ ; // vector of value
std::vector<itype> ja_ ; // pointer vector
int dim ;
};
//constructor
template <typename T>
constexpr MCSRmatrix<T>::MCSRmatrix( std::initializer_list<std::initializer_list<T>> rows)
{
this->dim = rows.size();
auto _rows = *(rows.begin());
aa_.resize(dim+1);
ja_.resize(dim+1);
if(dim != _rows.size()) for(auto i=0; i< A.dim ; i++)
{
//for(auto k=A.ja_.at(i) ; k <= A.ja_.at(i+1)-1 ; k++ )
auto k=A.ja_.at(i)-1;
do
{
b.at(i) += A.aa_.at(k)* x.at(A.ja_.at(k)-1);
k++ ;
}while (k < A.ja_.at(i+1)-1 ); // ;
}
return b;
{
throw std::runtime_error("error matrix must be square");
}
itype w = 0 ;
ja_.at(w) = dim+2 ;
for(auto ii = rows.begin(), i=1; ii != rows.end() ; ++ii, i++)
{
for(auto ij = ii->begin(), j=1, elemCount = 0 ; ij != ii->end() ; ++ij, j++ )
{
if(i==j)
aa_[i-1] = *ij ;
else if( i != j && *ij != 0 )
{
ja_.push_back(j);
aa_.push_back(*ij);
elemCount++ ;
}
ja_[i] = ja_[i-1] + elemCount;
}
}
for(auto& x : aa_ )
std::cout << x << ' ' ;
std::cout << std::endl;
for(auto& x : ja_ )
std::cout << x << ' ' ;
std::cout << std::endl;
}
template <typename T>
std::vector<T> operator*(const MCSRmatrix<T>& A, const std::vector<T>& x ) noexcept
{
std::vector<T> b(A.dim);
for(auto i=0; i < A.dim ; i++ )
b.at(i) = A.aa_.at(i)* x.at(i) ;
for(auto i=0; i< A.dim ; i++)
{
for(auto k=A.ja_.at(i) ; k < A.ja_.at(i+1)-1 ; k++ )
{
b.at(i) += A.aa_.at(k)* x.at(A.ja_.at(k));
}
}
return b;
}
and finally the main
# include "ModCSRmatrix.H"
using namespace std;
int main(){
std::vector<double> v1={0,1.3,4.2,0.8};
MCSRmatrix<double> m1 = {{1.01, 0 , 2.34,0}, {0, 4.07, 0,0},{3.12,0,6.08,0},{1.06,0,2.2,9.9} };
std::vector<double> v2 = m1*v1 ;
for(auto& x : v2)
cout << x << ' ' ;
cout << endl;
}
but the result is different from the result obtain in octave !
I've correct the code and now compile ! it give me the result :
0 5.291 25.536 9.68
but the correct result obtained using octave is :
9.8280 5.2910 25.5360 17.1600
the strange thing is that the same code written in Fortran works!
MODULE MSR
IMPLICIT NONE
CONTAINS
subroutine amuxms (n, x, y, a,ja)
real*8 x(*), y(*), a(*)
integer n, ja(*)
integer i, k
do 10 i=1, n
y(i) = a(i)*x(i)
10 continue
do 100 i = 1,n
do 99 k=ja(i), ja(i+1)-1
y(i) = y(i) + a(k) *x(ja(k))
99 continue
100 continue
return
end
END MODULE
PROGRAM MSRtest
USE MSR
IMPLICIT NONE
INTEGER :: i
REAL(KIND(0.D0)), DIMENSION(4) :: y, x= (/0.,1.3,4.2,0.8/)
REAL(KIND(0.D0)), DIMENSION(9) :: AA = (/ 1.01, 4.07, 6.08, 9.9, 0., 2.34, 3.12, 1.06, 2.2/)
INTEGER , DIMENSION(9) :: JA = (/6, 7, 7, 8, 10, 3, 1, 1, 3/)
WRITE(6,FMT='(4F8.3)') (x(I), I=1,4)
CALL amuxms(4,x,y,aa,ja)
WRITE(6,FMT='(4F8.3)') (y(I), I=1,4)
END PROGRAM
in the above code the value of aa and ja is given by the c++ constructor putting this member
template <typename T>
inline auto constexpr MCSRmatrix<T>::printMCSR() const noexcept
{
for(auto& x : aa_ )
std::cout << x << ' ' ;
std::cout << std::endl;
for(auto& x : ja_ )
std::cout << x << ' ' ;
std::cout << std::endl;
}
and call it at the end of constructor! now I have added the lines of the member at the end of constructor so if you try the constructor you get exactly the same vector written in the fortran code
thanks I followed your advice #Paul H. and rewrite the operator + as follow:
(I didn't change the ja_ indexing because in my class I have a lot of already more or less un-bugged method )
template <typename T>
std::vector<T> operator*(const MCSRmatrix<T>& A, const std::vector<T>& x ) noexcept
{
std::vector<T> b(A.dim);
for(auto i=0; i < A.dim ; i++ )
b.at(i) = A.aa_.at(i)* x.at(i) ;
for(auto i=0; i< A.dim ; i++)
{
//for(auto k=A.ja_.at(i) ; k <= A.ja_.at(i+1)-1 ; k++ )
auto k=A.ja_.at(i)-1;
do
{
b.at(i) += A.aa_.at(k)* x.at(A.ja_.at(k)-1);
k++ ;
}while (k < A.ja_.at(i+1)-1 ); // ;
}
return b;
}
as You can see I have subtracts 1 from all ja_ using as indices :
x.at(A.ja_.at(k)-1) instead of x.at(A.ja_.at(k))
different start of index K k=A.ja_.at(i)-1
and different end of cicle (I've used a do while instead of for)
The debugger is your friend! For future reference, here is a link to a very good blog post on debugging small programs: How to debug small programs.
There are a couple of off by one mistakes in your code. If you create the 4 x 4 matrix used as an example in the reference you linked to, you will see that the ja_ values you calculate are all off by one. The reason your Fortran version works is because arrays in Fortran are by default indexed starting from 1, not 0. So in class MCSRmatrix change
ja_.at(w) = dim+2;
to
ja_.at(w) = dim+1;
and
ja_.push_back(j);
to
ja_.push_back(j-1);
Then in your operator* method change
for(auto k=A.ja_.at(i) ; k < A.ja_.at(i+1)-1 ; k++ )
to
for(auto k = A.ja_.at(i); k < A.ja_.at(i+1); k++)