C++ matrix template class slice - c++

In order to learn about C++ templates I am writing a simple Matrix class. So far it has been working well, but I want to add the ability to slice the Matrix to extract a sub-matrix. I am struggling to figure out how to define the size of the return matrix. I have tried the following:
#include <cstdint>
#include <array>
#include <initializer_list>
template<typename T, std::size_t M, std::size_t N>
class Matrix
{
public:
Matrix(void): m_data{0} {}
Matrix(const std::initializer_list<std::initializer_list<T>> m)
{
for(auto i = m.begin(); i != m.end(); i++)
{
for(auto j = i->begin(); j != i->end(); j++)
{
(*this)(i - m.begin(), j - i->begin()) = *j;
}
}
}
T& operator()(const std::size_t i, const std::size_t j)
{
return m_data.at(i + j * N);
}
const T& operator()(const std::size_t i, const std::size_t j) const
{
return m_data.at(i + j * N);
}
template<std::size_t X, std::size_t Y>
Matrix<T,X,Y> slice(const std::size_t iStart, const std::size_t iEnd, const std::size_t jStart, const std::size_t jEnd)
{
Matrix<T,iEnd-iStart+1,jEnd-jStart+1> result;
for(std::size_t i = iStart; i <= iEnd; i++)
{
for(std::size_t j = jStart; j <= jEnd; j++)
{
result(i - iStart, j - jStart) = (*this)(i,j);
}
}
return result;
}
};
int main(void)
{
Matrix<double,3,3> m1 = {{1,2,3},{4,5,6},{7,8,9}};
Matrix<double,2,2> m2 = m1.slice(0,1,0,1);
return 0;
}
But I just get an error saying that 'iEnd' is not a constant expression. What would be the correct way to go about this?

You cannot use function parameters to instantiate templates. You need to pass in the sizes as template arguments to slice:
template<std::size_t iStart, std::size_t iEnd, std::size_t jStart, std::size_t jEnd,
std::size_t I = iEnd-iStart+1, std::size_t J = jEnd-jStart+1>
Matrix<T,I,J> slice()
{
Matrix<T,I,J> result;
///...
return result;
}

Related

Calling a Function but code says no matching function in C++

This is my code and the error which comes is
no matching function for call to to_reduced_row_echelon_form(float[((unsigned int)((int)r))][((unsigned int)((int)c))])'
#include <algorithm> // for std::swap
#include <cstddef>
#include <cassert>
// Matrix traits: This describes how a matrix is accessed. By
// externalizing this information into a traits class, the same code
// can be used both with native arrays and matrix classes. To use the
// default implementation of the traits class, a matrix type has to
// provide the following definitions as members:
//
// * typedef ... index_type;
// - The type used for indexing (e.g. size_t)
// * typedef ... value_type;
// - The element type of the matrix (e.g. double)
// * index_type min_row() const;
// - returns the minimal allowed row index
// * index_type max_row() const;
// - returns the maximal allowed row index
// * index_type min_column() const;
// - returns the minimal allowed column index
// * index_type max_column() const;
// - returns the maximal allowed column index
// * value_type& operator()(index_type i, index_type k)
// - returns a reference to the element i,k, where
// min_row() <= i <= max_row()
// min_column() <= k <= max_column()
// * value_type operator()(index_type i, index_type k) const
// - returns the value of element i,k
//
// Note that the functions are all inline and simple, so the compiler
// should completely optimize them away.
template<typename MatrixType> struct matrix_traits
{
typedef typename MatrixType::index_type index_type;
typedef typename MatrixType::value_type value_type;
static index_type min_row(MatrixType const& A)
{ return A.min_row(); }
static index_type max_row(MatrixType const& A)
{ return A.max_row(); }
static index_type min_column(MatrixType const& A)
{ return A.min_column(); }
static index_type max_column(MatrixType const& A)
{ return A.max_column(); }
static value_type& element(MatrixType& A, index_type i, index_type k)
{ return A(i,k); }
static value_type element(MatrixType const& A, index_type i, index_type k)
{ return A(i,k); }
};
// specialization of the matrix traits for built-in two-dimensional
// arrays
template<typename T, std::size_t rows, std::size_t columns>
struct matrix_traits<T[rows][columns]>
{
typedef std::size_t index_type;
typedef T value_type;
static index_type min_row(T const (&)[rows][columns])
{ return 0; }
static index_type max_row(T const (&)[rows][columns])
{ return rows-1; }
static index_type min_column(T const (&)[rows][columns])
{ return 0; }
static index_type max_column(T const (&)[rows][columns])
{ return columns-1; }
static value_type& element(T (&A)[rows][columns],
index_type i, index_type k)
{ return A[i][k]; }
static value_type element(T const (&A)[rows][columns],
index_type i, index_type k)
{ return A[i][k]; }
};
// Swap rows i and k of a matrix A
// Note that due to the reference, both dimensions are preserved for
// built-in arrays
template<typename MatrixType>
void swap_rows(MatrixType& A,
typename matrix_traits<MatrixType>::index_type i,
typename matrix_traits<MatrixType>::index_type k)
{
matrix_traits<MatrixType> mt;
typedef typename matrix_traits<MatrixType>::index_type index_type;
// check indices
assert(mt.min_row(A) <= i);
assert(i <= mt.max_row(A));
assert(mt.min_row(A) <= k);
assert(k <= mt.max_row(A));
for (index_type col = mt.min_column(A); col <= mt.max_column(A); ++col)
std::swap(mt.element(A, i, col), mt.element(A, k, col));
}
// divide row i of matrix A by v
template<typename MatrixType>
void divide_row(MatrixType& A,
typename matrix_traits<MatrixType>::index_type i,
typename matrix_traits<MatrixType>::value_type v)
{
matrix_traits<MatrixType> mt;
typedef typename matrix_traits<MatrixType>::index_type index_type;
assert(mt.min_row(A) <= i);
assert(i <= mt.max_row(A));
assert(v != 0);
for (index_type col = mt.min_column(A); col <= mt.max_column(A); ++col)
mt.element(A, i, col) /= v;
}
// in matrix A, add v times row k to row i
template<typename MatrixType>
void add_multiple_row(MatrixType& A,
typename matrix_traits<MatrixType>::index_type i,
typename matrix_traits<MatrixType>::index_type k,
typename matrix_traits<MatrixType>::value_type v)
{
matrix_traits<MatrixType> mt;
typedef typename matrix_traits<MatrixType>::index_type index_type;
assert(mt.min_row(A) <= i);
assert(i <= mt.max_row(A));
assert(mt.min_row(A) <= k);
assert(k <= mt.max_row(A));
for (index_type col = mt.min_column(A); col <= mt.max_column(A); ++col)
mt.element(A, i, col) += v * mt.element(A, k, col);
}
// convert A to reduced row echelon form
template<typename MatrixType>
void to_reduced_row_echelon_form(MatrixType& A)
{
matrix_traits<MatrixType> mt;
typedef typename matrix_traits<MatrixType>::index_type index_type;
index_type lead = mt.min_row(A);
for (index_type row = mt.min_row(A); row <= mt.max_row(A); ++row)
{
if (lead > mt.max_column(A))
return;
index_type i = row;
while (mt.element(A, i, lead) == 0)
{
++i;
if (i > mt.max_row(A))
{
i = row;
++lead;
if (lead > mt.max_column(A))
return;
}
}
swap_rows(A, i, row);
divide_row(A, row, mt.element(A, row, lead));
for (i = mt.min_row(A); i <= mt.max_row(A); ++i)
{
if (i != row)
add_multiple_row(A, i, row, -mt.element(A, i, lead));
}
}
}
// test code
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int r=0,c=0,i,j;
cout << "Enter no of rows of the matrix";
cin >> r;
cout << "Enter no of columns of the matrix";
cin >> c;
float l[r][c];
int p = 0, q = 0;
while (p < r) {
while (q < c) {
cin >> l[p][q];
q = q + 1;
}
p = p + 1;
q = 0;
}
to_reduced_row_echelon_form(l);
for (int i = 0; i < r; ++i)
{
for (int j = 0; j < c; ++j)
std::cout << l[i][j] << '\t';
std::cout << "\n";
}
system("pause");
return EXIT_SUCCESS;
}
calling the function whose code i took from https://rosettacode.org/wiki/Reduced_row_echelon_form
Variable length arrays (VLAs) like float l[r][c]; are not part of standard C++, so any attempt to reason when they might or might not work is fruitless.
The requirements of the type passed to to_reduced_row_echelon_form are clearly described in the comment starting 'Matrix traits'. You need to define a class with those features. Something like this
class MyMatrix
{
public:
MyMatrix(int r, int c) : r(r), c(c), l(r, std::vector<int>(c)) {}
std::vector<int>& operator[](int r) { return l[r]; }
const std::vector<int>& operator[](int r) const { return l[r]; }
using index_type = int;
using value_type = int;
value_type& operator()(index_type i, index_type j) { return l[i][j]; }
value_type operator()(index_type i, index_type j) const { return l[i][j]; }
index_type min_row() const { return 0; }
index_type max_row() const { return r - 1; }
index_type min_column() const { return 0; }
index_type max_column() const { return c - 1; }
private:
int r, c;
std::vector<std::vector<int>> l;
};
Not the most elegant or efficient code, but it does compile and hopefully works.
Then use it like this
MyMatrix l(r, c);
...
to_reduced_row_echelon_form(l);

Can one make a 2D matrix class where often accessed sizes, rows and cols, are const members and not getter functions?

I'm continually irritated that matrix dimensions are accessed with getters. Sure one can have public rows and cols but these should be const so that users of the Matrix class can't directly modify them. This is a natural way of coding but, because using const members is regarded, apparently incorrectly, as not possible without UB, results in people using non-const fields when they really should be consts that are mutable when needed such as resizing, or assigning.
What I'd like is something that can be used like this:
Matrix2D<double> a(2, 3);
int index{};
for (int i = 0; i < a.rows; i++)
for (int ii = 0; ii < a.cols; ii++)
a(i, ii) = index++;
but where a.rows=5; isn't compilable because it's const. And it would be great if the class can be included in vectors and other containers.
Now comes: Implement C++20's P0784 (More constexpr containers)
https://reviews.llvm.org/D68364?id=222943
It should be doable without casts or UB and can even be done when evaluating const expressions. I believe c++20 has made it doable by providing the functions std::destroy_at and std::construct_at. I have attached an answer using c++20 that appears to show it is indeed possible, and easily done, to provide const member objects in classes without an undue burden.
So the question is do the new constexpr functions in c++20, which provide the ability to destroy and then construct an object with different consts valid? It certainly appears so and it passes the consexpr lack of UB test.
If you want to have const properties you can try this :
class Matrix {
public:
const int rows;
const int cols;
Matrix(int _rows, int _cols) : rows(_rows), cols(_cols)
{
}
};
The rows and cols properties of a Matrix instance are initialized once in the constructor.
Well, here's my pass at using const rows and cols in a 2D matrix. It passes UB tests and can be used in collections like vector w/o weird restrictions. It's a partial implementation. No bounds checking and such but to provide a possible approach to the problem.
matrix2d.h
#pragma once
#include <memory>
#include <vector>
#include <initializer_list>
#include <stdexcept>
#include <array>
template<class T>
class Matrix2D
{
std::vector<T> v;
public:
const size_t rows;
const size_t cols;
constexpr Matrix2D(const Matrix2D& m) = default;
constexpr Matrix2D(Matrix2D&& m) noexcept = default;
explicit constexpr Matrix2D(size_t a_rows=0, size_t a_cols=0) : v(a_rows* a_cols), rows(a_rows), cols(a_cols) {}
constexpr Matrix2D(std::initializer_list<std::initializer_list<T>> list) : rows(list.size()), cols((list.begin())->size())
{
for (auto& p1 : list)
for (auto p2 : p1)
v.push_back(p2);
}
constexpr Matrix2D& operator=(const Matrix2D& mat)
{
std::vector<T> tmp_v = mat.v;
std::construct_at(&this->rows, mat.rows);
std::construct_at(&this->cols, mat.cols);
this->v.swap(tmp_v);
return *this;
}
constexpr Matrix2D& operator=(Matrix2D&& mat) noexcept
{
std::construct_at(&this->rows, mat.rows);
std::construct_at(&this->cols, mat.cols);
this->v.swap(mat.v);
return *this;
}
// user methods
constexpr T* operator[](size_t row) { // alternate bracket indexing
return &v[row * cols];
};
constexpr T& operator()(size_t row, size_t col)
{
return v[row * cols + col];
}
constexpr const T& operator()(size_t row, size_t col) const
{
return v[row * cols + col];
}
constexpr Matrix2D operator+(Matrix2D const& v1) const
{
if (rows != v1.rows || cols != v1.cols) throw std::range_error("cols and rows must be the same");
Matrix2D ret = *this;
for (size_t i = 0; i < ret.v.size(); i++)
ret.v[i] += v1.v[i];
return ret;
}
constexpr Matrix2D operator-(Matrix2D const& v1) const
{
if (rows != v1.rows || cols != v1.cols) throw std::range_error("cols and rows must be the same");
Matrix2D ret = *this;
for (size_t i = 0; i < ret.v.size(); i++)
ret.v[i] -= v1.v[i];
return ret;
}
constexpr Matrix2D operator*(Matrix2D const& v1) const
{
if (cols != v1.rows) throw std::range_error("cols of first must == rows of second");
Matrix2D v2 = v1.transpose();
Matrix2D ret(rows, v1.cols);
for (size_t row = 0; row < rows; row++)
for (size_t col = 0; col < v1.cols; col++)
{
T tmp{};
for (size_t row_col = 0; row_col < cols; row_col++)
tmp += this->operator()(row, row_col) * v2(col, row_col);
ret(row, col) = tmp;
}
return ret;
}
constexpr Matrix2D transpose() const
{
Matrix2D ret(cols, rows);
for (size_t r = 0; r < rows; r++)
for (size_t c = 0; c < cols; c++)
ret(c, r) = this->operator()(r, c);
return ret;
}
// Adds rows to end
constexpr Matrix2D add_rows(const Matrix2D& new_rows)
{
if (cols != new_rows.cols) throw std::range_error("cols cols must match");
Matrix2D ret = *this;
std::construct_at(&ret.rows, rows + new_rows.rows);
ret.v.insert(ret.v.end(), new_rows.v.begin(), new_rows.v.end());
return ret;
}
constexpr Matrix2D add_cols(const Matrix2D& new_cols)
{
if (rows != new_cols.rows) throw std::range_error("rows must match");
Matrix2D ret(rows, cols + new_cols.cols);
for (size_t row = 0; row < rows; row++)
{
for (size_t col = 0; col < cols; col++)
ret(row, col) = this->operator()(row, col);
for (size_t col = cols; col < ret.cols; col++)
ret(row, col) = new_cols(row, col - cols);
}
return ret;
}
constexpr bool operator==(Matrix2D const& v1) const
{
if (rows != v1.rows || cols != v1.cols || v.size() != v1.v.size())
return false;
for (size_t i = 0; i < v.size(); i++)
if (v[i] != v1.v[i])
return false;
return true;
}
constexpr bool operator!=(Matrix2D const& v1) const
{
return !(*this == v1);
}
// friends
template <size_t a_rows, size_t a_cols>
friend constexpr auto make_std_array(const Matrix2D& v)
{
if (a_rows != v.rows || a_cols != v.cols) throw std::range_error("template cols and rows must be the same");
std::array<std::array<T, a_cols>, a_rows> ret{};
for (size_t r = 0; r < a_rows; r++)
for (size_t c = 0; c < a_cols; c++)
ret[r][c] = v(r, c);
return ret;
}
};
Source.cpp
#include <vector>
#include <algorithm>
#include <iostream>
#include "matrix2d.h"
constexpr std::array<std::array<int, 3>, 4> foo()
{
Matrix2D<double> a(2, 3), b;
Matrix2D d(a);
int index{};
for (int i = 0; i < a.rows; i++)
for (int ii = 0; ii < a.cols; ii++)
a(i, ii) = index++;
b = a + a;
Matrix2D<int> z1{ {1},{3},{5} };
z1 = z1.add_cols({{2}, {4}, {6}});
z1 = z1.add_rows({{7,8}});
// z1 is now {{1,2},{3,4},{5,6},{7,8}}
Matrix2D<int> z2{ {1,2,3},{4,5,6} };
Matrix2D<int> z3 = z1 * z2; // z3: 4 rows, 3 cols
// from separate math program product of z1 and z2
Matrix2D<int> ref{ {9,12,15},{19,26,33},{29,40,51},{39,54,69} };
// test transpose
Matrix2D<int> tmp = ref.transpose(); // tmp: now 3 rows, 4 cols
if (ref == tmp) throw; // verify not equal
tmp = tmp.transpose(); // now ref==tmp==z3
if (ref != tmp || ref != z3) throw;
// Check using vector of matrixes, verify sort on row size works
std::vector<Matrix2D<int>> v;
v.push_back(z1);
v.push_back(z2);
v.push_back(z1);
v.push_back(z2);
std::sort(v.begin(), v.end(), [](auto const& m1, auto const& m2) {return m1.rows < m2.rows; });
for (size_t i = 0; i < v.size() - 1; i++)
if (v[i].rows > v[i + 1].rows) throw;
// return constexpr of 2D std::array
std::array<std::array<int, 3>, 4> array = make_std_array<4,3>(ref); // ref must have 4 rows, 3 cols
return array;
}
int main()
{
auto print = [](auto& a) {
for (auto& x : a)
{
for (auto y : x)
std::cout << y << " ";
std::cout << '\n';
}
std::cout << '\n';
};
// run time
auto zz = foo();
print(zz);
// validating no UB in Matrix2D;
// and creating a nested, std::array initialized at compile time
//constexpr std::array<std::array<int, 3>, 4> x = foo();
constexpr std::array<std::array<int, 3>, 4> x = foo(); // compile time
print(x);
return 0;
}

Matrix determinant implementation in template matrix class

I'm working on a project which requires me to write my own matrix class implementation. I decided to implement the matrix class as a template to provide compile-time error-checking in the following way:
template<size_t N, size_t M> // N × M matrix
class Matrix
{
// implementation...
};
I managed to implement basic operations such as addition/subtraction, transpose and multiplication. However, I'm having trouble implementing the determinant. I was thinking of implementing it recursively using the Laplace expansion, so I must first implement a way to calculate the i,j minor of a matrix. The problem is, the minor of an N × N matrix is an (N-1) × (N-1) matrix. The following does not compile: (error message is Error C2059 syntax error: '<', pointing to the first line in the function)
template<size_t N>
Matrix<N-1, N-1> Minor(const Matrix<N, N>& mat, size_t i, size_t j)
{
Matrix<N-1, N-1> minor;
// calculate i,j minor
return minor
}
How could I go around this and calculate the minor, while keeping the templated form of the class?
EDIT: I was asked to provide a working example. Here is the relevant part of my code, I tried to keep it as minimal as possible. My Matrix class uses a Vector class, which I also wrote myself. I removed any unrelated code, and also changed any error-checks to asserts, as the actual code throws an exception class, which again was written by me.
Here is the Vector.h file:
#pragma once
#include <vector>
#include <cassert>
template<size_t S>
class Vector
{
public:
Vector(double fInitialValue = 0.0);
Vector(std::initializer_list<double> il);
// indexing range is 0...S-1
double operator[](size_t i) const;
double& operator[](size_t i);
private:
std::vector<double> m_vec;
};
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
template<size_t S>
Vector<S>::Vector(double fInitialValue) : m_vec(S, fInitialValue)
{
}
template<size_t S>
Vector<S>::Vector(std::initializer_list<double> il) : m_vec(il)
{
assert(il.size() == S);
}
template<size_t S>
double Vector<S>::operator[](size_t i) const
{
return m_vec[i];
}
template<size_t S>
double& Vector<S>::operator[](size_t i)
{
return m_vec[i];
}
And here is the Matrix.h file:
#pragma once
#include "Vector.h"
template<size_t N, size_t M>
class Matrix
{
public:
Matrix(double fInitialValue = 0.0);
Matrix(std::initializer_list<Vector<M>> il);
// indexing range is 0...N-1, 0...M-1
Vector<M> operator[](int i) const;
Vector<M>& operator[](int i);
double Determinant() const;
private:
std::vector<Vector<M>> m_mat; // a collection of row vectors
template <size_t N>
friend Matrix<N - 1, N - 1> Minor(const Matrix<N, N>& mat, size_t i, size_t j);
};
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
template<size_t N, size_t M>
Matrix<N, M>::Matrix(double fInitialValue)
: m_mat(N, Vector<M>(fInitialValue)) {}
template<size_t N, size_t M>
Matrix<N, M>::Matrix(std::initializer_list<Vector<M>> il) : m_mat(il)
{
assert(il.size() == N);
}
template<size_t N, size_t M>
Vector<M> Matrix<N, M>::operator[](int i) const
{
return m_mat[i];
}
template<size_t N, size_t M>
Vector<M>& Matrix<N, M>::operator[](int i)
{
return m_mat[i];
}
template<size_t N, size_t M>
double Matrix<N, M>::Determinant() const
{
assert(N == M);
if (N == 2) {
return m_mat[0][0] * m_mat[1][1] - m_mat[0][1] * m_mat[1][0];
}
double det = 0;
for (size_t j = 0; j < N; j++) {
if (j % 2) {
det += m_mat[0][j] * Minor((*this), 0, j).Determinant();
}
else {
det -= m_mat[0][j] * Minor((*this), 0, j).Determinant();
}
}
return det;
}
template <size_t N>
Matrix<N - 1, N - 1> Minor(const Matrix<N, N>& mat, size_t i, size_t j)
{
Matrix<N - 1, N - 1> minor;
for (size_t n = 0; n < i; n++) {
for (size_t m = 0; m < j; m++) {
minor[n][m] = mat[n][m];
}
}
for (size_t n = i + 1; n < N; n++) {
for (size_t m = 0; m < j; m++) {
minor[n - 1][m] = mat[n][m];
}
}
for (size_t n = 0; n < i; n++) {
for (size_t m = j + 1; m < N; m++) {
minor[n][m - 1] = mat[n][m];
}
}
for (size_t n = i + 1; n < N; n++) {
for (size_t m = j + 1; m < N; m++) {
minor[n - 1][m - 1] = mat[n][m];
}
}
return minor;
}
Compiling these along with a simple main.cpp file:
#include "Matrix.h"
#include <iostream>
int main() {
Matrix<3, 3> mat = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
std::cout << mat.Determinant();
}
produces - Error C2760 syntax error: unexpected token '<', expected 'declaration' ...\matrix.h 67
EDIT2: Apparently I had written the template arguments as <N - 1><N - 1> instead of <N -1, N-1> in the implementation of the Minor function. Changing that fixed the error, but introduced a new one - compilation hangs, and after a minute or so I get Error C1060 compiler is out of heap space ...\matrix.h 65

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;
}

Weird segmentation fault 11

There are two files:
Matrix.hpp:
template <typename T>
class Matrix {
private:
size_t rows = 0;
size_t cols = 0;
T* data = nullptr;
public:
Matrix() = default;
~Matrix();
Matrix(size_t n, size_t m);
T& operator() (size_t i, size_t j);
};
template <typename T>
Matrix<T>::Matrix(size_t n, size_t m) : rows(n), cols(m) {
try {
data = new T[rows*cols];
} catch (const std::bad_alloc& e) {
std::exit(EXIT_FAILURE);
}
};
template <typename T>
Matrix<T>::~Matrix() {
delete[] data;
}
template <typename T>
T& Matrix<T>::operator()(size_t i, size_t j) {
if (i < rows && i >= 0 && j < cols && j >= 0) {
return data[(i+1)*cols + (j+1)];
} else {
throw std::logic_error("matrix indices out of range");
}
}
and
Main.cpp:
#include <iostream>
#include "Matrix.hpp"
int main() {
size_t n, k;
std::cin >> n >> k;
Matrix<long double> m = {n, k};
for (size_t i = 0; i < m.getNumRows(); ++i) {
for (size_t j = 0; j < m.getNumCols(); ++j) {
std::cin >> m(i,j);
}
}
for (size_t i = 0; i < m.getNumRows(); ++i) {
std::cout << "\n";
for (size_t j = 0; j < m.getNumCols(); ++j) {
std::cout << m(i, j) << " ";
}
}
return 0;
}
When I'm entering something like:
1 2 3 4
I can get
3 4
as answer but sometimes the same input causes Segmentation fault 11 moreover when I'm changing template argument from long double to int, error disappears. How can I fix it?
Your operator function is accessing data outside its bounds.
Passing (0,0) to the function should return data[0]. Currently it returns data[2].
Change line
return data[(i+1)*cols+(j+1)];
to
return data[i*cols+j];