Efficent Sum and Assignment operator overloading in C++ - c++

Hi I'm implementing a matrix class in c++
I know that there are great libraries that do that like opencv but I need to do that myself.
For example if I implement the sum I can do like this
class Mat{
public:
double* data;
int rows,cols;
Mat(int r,int c):rows(r),cols(c){
data = new double[r*c];
}
};
void Sum(Mat& A,Mat& B,Mat& C){
for (int i = 0; i < A.rows*A.cols; ++i){
C.data[i] = A.data[i]+B.data[i];
}
}
int main(){
//Allocate Matrices
Mat A(300,300);
Mat B(300,300);
Mat C(300,300);
//do the sum
sum(A,B,C);
}
I would like to get something more readable like this but without losing efficiency
C = A + B
This way C is reallocated every time and I don't want that
Thank you for your time

Delay the calculation.
class MatAccess {
friend class Mat;
friend class MatOpAdd;
virtual double operator[](int index) const = 0;
};
class MatOpAdd: public MatAccess {
friend class Mat;
private:
const MatAccess& left;
const MatAccess& right;
MatOpAdd(const MatAccess& left, const MatAccess& right):
left(left), right(right) {}
double operator[](int index) const {
return left[index] + right[index];
}
};
class Mat: public MatAccess{
public:
double* data;
int rows,cols;
Mat(int r,int c):rows(r),cols(c){
data = new double[r*c];
}
MatOpAdd operator +(const MatAccess& other) {
return MatOpAdd(*this, other);
}
const Mat& operator = (const MatAccess& other) {
for(int i = 0; i < rows*cols; ++i) {
data[i] = other[i];
}
return *this;
}
private:
double operator[](int index) const {
return data[index];
}
double& operator[](int index) {
return data[index];
}
};
int main(){
//Allocate Matrices
Mat A(300,300);
Mat B(300,300);
Mat C(300,300);
//do the sum
C = A + B;
}
Now the '+' calculation will be done in the "operator="
Things I would change:
MatAccess should include the dimensions (rows,cols).
Mat adding constructors and operator= or make it not copyable
Mat::operator+ and Mat::operator= check for equal rows,col
delete memory when not used anymore or
use std::vector for simpler memory managment.
Created a bigger example here: https://gist.github.com/KoKuToru/1d23af4bbf0b2bc89893

Related

Writing a C++ iterator for a sparse matrix class

I'm attempting to get a basic constant forward-iterator to work in C++.
namespace Rcpp {
class SparseMatrix {
public:
IntegerVector i, p;
NumericVector x;
int begin_col(int j) { return p[j]; };
int end_col(int j) { return p[j + 1]; };
class iterator {
public:
int index;
iterator(SparseMatrix& g) : parent(g) {}
iterator(int ind) { index = ind; }; // ERROR!
bool operator!=(int x) const { return index != x; };
iterator operator++(int) { ++index; return (*this); };
int row() { return parent.i[index]; };
double value() { return parent.x[index]; };
private:
SparseMatrix& parent;
};
};
}
My intention is to use the iterator in contexts similar to the following:
// sum of values in column 7
Rcpp::SparseMatrix A(nrow, ncol, fill::random);
double sum = 0;
for(Rcpp::SparseMatrix::iterator it = A.begin_col(7); it != A.end_col(7); it++)
sum += it.value();
Two questions:
The compiler throws an error on the line indicated above: uninitialized reference member in 'class Rcpp::SparseMatrix&' [-fpermissive]. How can this be fixed?
How might double value() { return parent.x[index]; }; be re-worked to return a pointer to the value rather than a copy of the value?
A little context on the SparseMatrix class: like a dgCMatrix in R, this object of class SparseMatrix consists of three vectors:
i holds row pointers for every element in x
p gives indices in i which correspond to the start of each column
x contains non-zero values
Thanks to #Evg, here's the solution:
namespace Rcpp {
class SparseMatrix {
public:
IntegerVector i, p;
NumericVector x;
class iterator {
public:
int index;
iterator(SparseMatrix& g, int ind) : parent(g) { index = ind; }
bool operator!=(iterator x) const { return index != x.index; };
iterator& operator++() { ++index; return (*this); };
int row() { return parent.i[index]; };
double& value() { return parent.x[index]; };
private:
SparseMatrix& parent;
};
iterator begin_col(int j) { return iterator(*this, p[j]); };
iterator end_col(int j) { return iterator(*this, p[j + 1]); };
};
}
And it can be used as follows, for instance, to calculate colSums:
//[[Rcpp::export]]
Rcpp::NumericVector Rcpp_colSums(Rcpp::SparseMatrix& A) {
Rcpp::NumericVector sums(A.cols());
for (int i = 0; i < A.cols(); ++i)
for (Rcpp::SparseMatrix::iterator it = A.begin_col(i); it != A.end_col(i); it++)
sums(i) += it.value();
return sums;
}
And, the above function is faster than RcppArmadillo, RcppEigen, and R::Matrix equivalents when microbenchmarked from R!
Edit:
The above syntax is inspired by Armadillo. I've come to realize that a slightly different syntax (which involves fewer constructions) gives an iterator similar to Eigen:
class col_iterator {
public:
col_iterator(SparseMatrix& ptr, int col) : ptr(ptr) { indx = ptr.p[col]; max_index = ptr.p[col + 1]; }
operator bool() const { return (indx != max_index); }
col_iterator& operator++() { ++indx; return *this; }
const double& value() const { return ptr.x[indx]; }
int row() const { return ptr.i[indx]; }
private:
SparseMatrix& ptr;
int indx, max_index;
};
Which can then be used like this:
int col = 0;
for (Rcpp::SparseMatrix::col_iterator it(A, col); it; ++it)
Rprintf("row: %3d, value: %10.2e", it.row(), it.value());

C++ use of deleted function with unique_ptr, make_unique

A pseudo class performing matrix additon using std::unique_ptr as the data member to represent the elements. M-rows, N columns. Class is templated for type, M, N. I am trying to perform a matrix addition (C = A+B) and keep getting this error from "error: use of deleted function". I am trying to crunch the fundamentals of C++ with a complication of smart_ptrs, any pointers regarding this error is appreciated.
All the participating functions (in the error) are shown below:
class Matrix{
private:
std::unique_ptr<T[]> array=std::make_unique<T[]>(M*N);
public:
Matrix();
Matrix( const Matrix& other );
inline Matrix operator+( const Matrix& other ) const;
const Matrix& operator=( const Matrix< M, N, T >& other );
}
Matrix::Matrix(const Matrix& other)
{
*this = other;
}
inline Matrix
Matrix::operator+( const Matrix& other ) const
{
Matrix result(*this);
result += other;
return result;
}
const Matrix&
Matrix::operator=( const Matrix& other )
{
if(this != &other)
this->array = std::move(other.array);
return *this;
}
Error reporting places:
error: use of deleted function ‘matlib::Matrix<2ul, 2ul, double>::Matrix(const matlib::Matrix<2ul, 2ul, double>&)’
Matrix< M, N, T > result(*this);
error: use of deleted function ‘std::unique_ptr<_Tp [], _Dp>& std::unique_ptr<_Tp [], _Dp>::operator=(const std::unique_ptr<_Tp [], _Dp>&) [with _Tp = double; _Dp = std::default_delete<double []>]’
this->array = std::move(other.array);
Thanks in advance.
In general, most of your errors originate from that std::unique_ptr has no copy constructor. This is so that it can efficiently manage the scope of the pointer inside of it. One way around this is to create a new std::unique_ptr, and copy all individual values over.
I've written an example class for you, perhaps it can help.
#include <memory>
template <class T>
class Matrix
{
private:
std::unique_ptr<T[]> data = nullptr;
size_t height, width;
public:
Matrix(size_t h, size_t w)
: height(h), width(w)
{
if(h*w == 0) return;
data = std::make_unique<T[]>(h*w);
}
Matrix(const Matrix& other)
{
height = other.height;
width = other.width;
data = std::make_unique<T[]>(height * width);
for(size_t i = 0; i < height; i++)
{
for(size_t j = 0; j < width; j++)
{
(*this)(j, i) = other(j,i);
}
}
}
Matrix operator=( const Matrix& other )
{
if(this == &other)
{
return *this;
}
height = other.height;
width = other.width;
data.reset(std::make_unique<T[]>(other.height * other.width));
for(size_t i = 0; i < height; i++)
{
for(size_t j = 0; j < width; j++)
{
(*this)(j, i) = other(j,i);
}
}
return *this;
}
T & operator()(size_t x, size_t y)
{
//If data is nullptr then this is undefined behaviour.
//Consider adding a throw or assert here
return data[y * height + x];
}
T operator()(size_t x, size_t y) const
{
//If data is nullptr then this is undefined behaviour.
//Consider adding a throw or assert here
return data[y * height + x];
}
size_t getHeight() const
{
return height;
}
size_t getWidth() const
{
return width;
}
};
As a final statement, if what you want to do is to create matrices for mathematical purposes, I suggest you give them static sizes for performance reasons. Adding in mathematical operators to a class like this involves additional logic for the cases where dimensions are mismatched. Statically sized matrices will solve this by themselves due to their typing. You can still do it like this, but be wary of any edge cases.

proxy class in rvalue - how to implement assignment operator?

Suppose I have a simple vector class where elements are accessed through a proxy class.
Vector class:
class vec {
public:
vec(int len) {
length = len;
data = new double [len];
}
proxy operator[](int i) {
if (i >= 0 && i < length) {
return proxy(i, data);
}
else {
std::cerr << "AHHHH!\n";
exit(1);
}
}
private:
int length;
double * data;
};
Proxy class:
class proxy {
public:
proxy(int i, double * d) {
index = i;
data = d;
}
void operator=(double rhs) {
data[index] = rhs;
}
private:
int index;
double * data;
};
How can I assign elements from the vector (or rather, from the proxy) to a variable of type double? In other words, how do I accomplish the following:
int main() {
vec a(2);
double x = 3.14;
a[0] = x; // Works!
x = a[0]; // How to make work?
return 0;
}
Unfortunately, I can't write something like:
friend double operator=(double & lhs, const proxy & p) { ... }
since operator= must be a member.
Add a conversion function to your proxy class:
class proxy
{
public:
operator double() const { return data[index]; }
// ...
};

Operator[][] overload - to change the value

I have a matrix class and wish to do this operation:
matrix m1(2,4); //creates matrix of size 2,4 full of 1's
m1[1][2]=4; //I wish the value in place m1- row 1 col 2 will be 4
this is example to how my code is writen (found on this site ty Seth Carnegie!!)
class matrix{
public:
matrix() {
_arrayofarrays = new int*[10];
for(int i = 0; i < 10; ++i)
_arrayofarrays[i] = new int[10];
}
class Proxy {
public:
Proxy(int* _array) : _array(_array) { }
int operator[](int index) {
return _array[index];
}
private:
int* _array;
};
Proxy operator[](int index) {
return Proxy(_arrayofarrays[index]);
}
private:
int** _arrayofarrays;
};
the problem is : m1[2][4] returns a int and offcorce if i chage the returned value it does not change the m1 value
how can I do it so the value will be changed? (maybe return int& or something?)
return int& instead of int

subarray through index: how to?

I am trying to implement a simple matlab-like array (now only one dimension actually), what I tried to do is to implement the following matlab codes:
a=1:10;
ind=find(a>5);
a[ind]=5;
I know that the std has valarray to do this through a slice array. I do not know much details on it. The code is:
#include <iostream>
using namespace std;
template <typename T> class array
{
public:
int m,n; //two dimensional at most
T *pdata;
//construct the array
array(){m=n=0;pdata=NULL;} //default is empty matrix
array(T a){m=n=1;pdata=new T[1];*pdata=a;} //array for scalar: array a=10;
array(int m0,int n0=1) {m=m0;n=1;pdata=new T[m];}
array(const array& a,int len=-1);
//destructor
~array() {delete []pdata;}
//operator overloading
array<T>& operator+=(T s);
T& operator[](int i) {return pdata[i];}
array<T>& operator[](array<int> ind);
array<T>& operator=(const array<T>& a);
array<T>& operator=(T a) {for(int i=0;i<m;i++) pdata[i]=a;return *this;}
array<bool> operator>(T a);
array<bool> operator<(T a);
array<bool> operator==(T a);
};
//copy a part of the other array
template <typename T> array<T>::array<T>(const array<T>& a,int len)
{
if(len==-1) len=a.m*a.n;
if(len==0) {m=0;n=0;pdata=NULL;}
if(len>0)
{
m=len;n=1;
pdata=new T[len];
for(int i=0;i<len;i++) pdata[i]=a.pdata[i];
}
}
template <typename T> array<T>& array<T>::operator +=(T s)
{
for(int i=0;i<m*n;i++) pdata[i]+=s;
return *this;
}
//this function does not meet the purpose, it returns a reference to a temp obj
template <typename T> array<T>& array<T>::operator[](array<int> ind)
{
array<T> ret(ind.m,ind.n);
for(int i=0;i<ind.m*ind.n;i++)
{
ret.pdata[i] = pdata[ind.pdata[i]];
}
return ret;
}
template <typename T> array<bool> array<T>::operator>(T a)
{
array<bool> res(m*n);
for(int i=0;i<m*n;i++) res.pdata[i]=pdata[i]>a;
return res;
}
//helper function
array<int> find(array<bool> a)
{
array<int> ret(a.m,a.n); //first use the same size space
int len=0;
for(int i=0;i<a.m*a.n;i++)
{
if(a.pdata[i]) {ret.pdata[len]=i;len++;}
}
return array<int>(ret,len);
}
/*ostream& operator<<(array<T>& a)
{
ostream os;
for(int i=0;i<a.m*a.n;i++) os>>a[i]>>'\t';
return os;
}*/
int main()
{
array<float> a(10);
for(int i=0;i<10;i++) a[i]=i;
for(i=0;i<10;i++) cout<<a[i]<<'\t';
cout<<endl;
array<int> ind=find(a>5);
for(i=0;i<ind.m;i++) cout<<ind[i]<<'\t';
cout<<endl;
a[ind]=5;//this will not work on the original array
//how do we support this????undefined
for(i=0;i<10;i++) cout<<a[i]<<'\t';
cout<<endl;
return 0;
}
The final a is not changed at all since we are working on a temp array.
I know the function operator"> is not properly implemented, but I do not know how to do this. Anyone can give me a hint? Thanks
I would create an ArraySlice class, and return an instance of that from operator []. This class would have a reference to the original Array, and would need to re-implement most members as forward calls to the Array. For instance, ArraySlice::operator[] would call Array::operator[] with the appropriate index.
I think that for shared arrays the best solution is to have a single type for both the original (full) matrix and the "views".
By parametrizing the element access you can have the same generic code work for both, also if you add an optional std::vector element inside the class that will contain the actual data for the original full matrix then memory handling becomes automatic.
This is a small implementation of this idea... for the selection I've used an std::vector of pairs of integers; assigning to ArraySelection will use the element access operator so it will work both for the original matrix or for views.
The main program allocates a 10x10 matrix, and then creates four different 5x5 views with elements with coordinates that are even/even, even/odd, odd/even and odd/odd.
Those views are set to 4 different constant values.
Then a selection is done on the full matrix and an assignment is done on the selected elements. Finally the original full matrix is printed.
#include <stdexcept>
#include <vector>
#include <functional>
#include <algorithm>
#include <stdio.h>
typedef std::vector< std::pair<int, int> > Index;
template<typename T>
struct Array;
template<typename T>
struct ArraySelection
{
Array<T>& a;
Index i;
ArraySelection(Array<T>& a)
: a(a)
{
}
ArraySelection& operator=(const T& t)
{
for (int j=0,n=i.size(); j<n; j++)
a(i[j].first, i[j].second) = t;
return *this;
}
};
template<typename T>
struct Array
{
int rows, cols;
std::vector<T*> rptr;
int step;
std::vector<T> data; // non-empty if data is owned
T& operator()(int r, int c)
{
return rptr[r][c * step];
}
Array(int rows, int cols,
Array *parent = NULL,
int row0=0, int rowstep=1,
int col0=0, int colstep=1)
: rows(rows), cols(cols), rptr(rows)
{
if (parent == NULL)
{
// Owning matrix
data.resize(rows*cols);
for (int i=0; i<rows; i++)
rptr[i] = &data[i*cols];
step = 1;
}
else
{
// View of another matrix
for (int i=0; i<rows; i++)
rptr[i] = &((*parent)(row0 + i*rowstep, col0));
step = colstep;
}
}
template<typename F>
ArraySelection<T> select(const F& f)
{
Index res;
for (int i=0; i<rows; i++)
for (int j=0; j<cols; j++)
if (f((*this)(i, j)))
res.push_back(std::make_pair(i, j));
ArraySelection<T> ar(*this);
ar.i.swap(res);
return ar;
}
// Copy construction of a full matrix makes a full matrix,
// Copy construction of a view creates a view
Array(const Array& other)
: rows(other.rows), cols(other.cols), rptr(other.rptr), step(other.step)
{
if (other.data)
{
data = other.data;
for (int i=0; i<rows; i++)
rptr[i] = &data[i*cols];
}
}
// Assignment is element-by-element optionally with conversion
template<typename U>
Array& operator=(const Array<U>& other)
{
if (other.rows != rows || other.cols != cols)
throw std::runtime_error("Matrix size mismatch");
for(int i=0; i<rows; i++)
for (int j=0; j<cols; j++)
(*this)(i, j) = other(i, j);
return *this;
}
};
int main()
{
Array<double> a(10, 10);
Array<double> a00(5, 5, &a, 0, 2, 0, 2);
Array<double> a01(5, 5, &a, 0, 2, 1, 2);
Array<double> a10(5, 5, &a, 1, 2, 0, 2);
Array<double> a11(5, 5, &a, 1, 2, 1, 2);
for (int i=0; i<5; i++)
for (int j=0; j<5; j++)
{
a00(i, j) = 1.1;
a01(i, j) = 2.2;
a10(i, j) = 3.3;
a11(i, j) = 4.4;
}
a.select(std::binder2nd< std::greater<double> >(std::greater<double>(), 3.5)) = 0;
for (int i=0; i<10; i++)
{
for (int j=0; j<10; j++)
{
printf(" %0.3f", a(i, j));
}
printf("\n");
}
return 0;
}
Andrea, Thank you for the hint. Mostly what you said makes sense and really helps. I create another ind_array and keep the original array's address and now it works!
The only change is the operator[] now returns the ind_array. and the operator= for ind_array is defined to do the real assignment. (to make it simple, I now removed the second dimension)
Here is the modified code:
#include <iostream>
using namespace std;
template <typename T> class ind_array;
template <typename T> class array
{
public:
int len; //two dimensional at most
T *pdata;
//construct the array
array(){len=0;pdata=NULL;} //default is empty matrix
//array(T a){len=1;pdata=new T[1];*pdata=a;} //array for scalar: array a=10;
array(int m0) {len=m0;pdata=new T[len];}
array(const array& a,int len0=-1);
//destructor
~array() {delete []pdata;}
int size() const {return len;}
//operator overloading
array<T>& operator+=(T s);
T& operator[](int i) {return pdata[i];}
ind_array<T> operator[](const array<int>& ind);//{return (ind_array(ind,pdata));}
array<T>& operator=(const array<T>& a);
array<T>& operator=(T a) {for(int i=0;i<len;i++) pdata[i]=a;return *this;}
array<bool> operator>(T a);
array<bool> operator<(T a);
array<bool> operator==(T a);
};
//Index array or similar indirect-array as in valarray
//this class shall keeps the array's address and the index
template <typename T> class ind_array
{
array<int> ind; //an index array
T* ptr; //a pointer to the original data
public:
int size() const {return ind.size();}
void operator=(T a){for(int i=0;i<size();i++) ptr[ind[i]]=a;} //assignment a value to a subarray
//how to construct the indx array then?
//according to valarry, the default constructor shall be prohibited
ind_array(const array<int>& indx,T* pt):ind(indx),ptr(pt){} //default constructor
};
//copy a part of the other array
template <typename T> array<T>::array<T>(const array<T>& a,int len0)
{
if(len0==-1) len0=a.len;
if(len0==0) {len=0;pdata=NULL;}
if(len0>0)
{
len=len0;
pdata=new T[len];
for(int i=0;i<len;i++) pdata[i]=a.pdata[i];
}
}
template <typename T> array<T>& array<T>::operator +=(T s)
{
for(int i=0;i<len;i++) pdata[i]+=s;
return *this;
}
//this function does not meet the purpose, it returns a reference to a temp obj
//now we change it to return a indx_array which stores the original array's address
template <typename T> ind_array<T> array<T>::operator[](const array<int>& ind)
{
/*array<T> ret(ind.len);
for(int i=0;i<ind.len;i++)
{
ret.pdata[i] = pdata[ind.pdata[i]];
}
return ret;*/
return (ind_array<T>(ind,pdata)); //call the constructor
}
template <typename T> array<bool> array<T>::operator>(T a)
{
array<bool> res(len);
for(int i=0;i<len;i++) res.pdata[i]=pdata[i]>a;
return res;
}
//helper function
array<int> find(array<bool> a)
{
array<int> ret(a.len); //first use the same size space
int len=0;
for(int i=0;i<a.len;i++)
{
if(a.pdata[i]) {ret.pdata[len]=i;len++;}
}
return array<int>(ret,len);
}
/*ostream& operator<<(array<T>& a)
{
ostream os;
for(int i=0;i<a.m*a.n;i++) os>>a[i]>>'\t';
return os;
}*/
int main()
{
array<float> a(10);
for(int i=0;i<10;i++) a[i]=i;
for(i=0;i<10;i++) cout<<a[i]<<'\t';
cout<<endl;
array<int> ind=find(a>5);
for(i=0;i<ind.len;i++) cout<<ind[i]<<'\t';
cout<<endl;
a[ind]=5;//this will not work on the original array
//how do we support this????undefined
for(i=0;i<10;i++) cout<<a[i]<<'\t';
cout<<endl;
return 0;
}