I'm making a matrix calculator as a project for my C++ class in college and I'm not sure how to design the classes for it. My problem is that one of the traits of this program has to be that sparse and dense matrices should be stored in different ways for memory efficiency (dense as typical 2D array or vector, sparse in CSR format for example), but I need to handle both of the types in same way.
So far I was thinking of something like have abstract class 'MatrixWrapper', which should contain all the shared algorithms for adding, multiplying, GEM, and so on. And then have classes 'MatrixDense' and 'MatrixSparse', which would both inherit from 'MatrixWrapper' and therefor have same interface (shown in code below). But that's where I got stuck, because with this approach when I tried implementing the algorithms in 'MatrixWrapper' I didn't know with which of the two matrices I'd be working. I'm just not sure how to solve this or even may approach is correct.
class MatrixWrapper {
public:
// shared algorithms
/* for example
void addMatrix ( const ??? &x ) {
...
}
*/
}
class MatrixDense : public MatrixWrapper {
public:
//constructor, destructor, ...
private:
vector< vector<double> > matrix;
}
class MatrixSparse : public MatrixWrapper {
public:
//constructor, destructor, ...
private:
struct CSR {
...
};
CSR matrix;
}
I was maybe thinking about adding 2D array to the 'MatrixWrapper' along side with abstract method setValue() and then in 'MatrixSparse' and 'MatrixDense' every time just setting the values of this array using this method and then just working with that 2D array in 'MatrixWrapper', but I'm not sure how to implement that or even if that's the right approach.
Implement all binary operators using non-member functions. Either global functions, or functions inside an unrelated class:
// Option 1
void add(
MatrixWrapper& result,
const MatrixWrapper& operand1,
const MatrixWrapper& operand2);
// Option 2
struct WrapperForMatrixOperations // I don't know why you might want this class to exist
{
static // or maybe not static
void add(
MatrixWrapper& result,
const MatrixWrapper& operand1,
const MatrixWrapper& operand2);
};
The reason is, your algorithm will probably return a "dense" matrix when adding a dense and a sparse matrix:
dense + sparse = dense
sparse + sparse = sparse
sparse + dense = dense <- problem!
dense + dense = dense
This cannot work if it is implemented as a non-const member function.
You should also decide how you want to create your matrices - maybe each binary operation should allocate a new matrix and return it by shared_ptr?
Related
I just started learning C++ and I'm used to Java paradigms, so I'm not sure how this should be done:
I need to represent a vector of products of two different types: packaged and fresh food. They have some common fields with a single implementation (availability, re-stock quantity etc), but they have also different fields and functions with different return types.
I.E: fresh foods may have a boolean field needsRefrigeration, other
products may have an integer representing a category (food, cleaning,
bricolage, forniture...).
In Java I would create a Product object with the common fields and a PackagedProduct (extending Product) plus a FreshProduct (also extending Product) with their particular fields. Then I'd place every product in a vector and accessed as Product when I need the common fields, safely-casted (with instanceof) to the right class when I need to access the child's fields.
I know this is not the right way in C++ and I don't want to force java programming paradigms to C++.
I can imagine:
create all the functions required by all the cildren as virtual in the parent and add a field in the parent representing the type of the cild, so it can safely casted
create a wrapper object containing two different vectors, each one of the type of a child object and return the values in the correct order, eventually using a third int vector.
I think these solutions are really bad, and I'm almost sure there must be a better way, but I can't imagine it. Can you help me?
What's the right way to do this?
I need to represent a vector of products of two different types: packaged and fresh food.
Do you really need both types of product in the same vector? Can't you have two vectors?
std::vector<PackagedProduct> packaged;
std::vector<FreshProduct> fresh;
packaged.emplace_back(1, 2, 3);
fresh.emplace_back(4, 5, 6);
This will be by far the most efficient solution. (Less indirections keep the prefetcher happy.)
If you absolutely need both kinds of products in the same vector, you must use indirection:
std::vector<std::unique_ptr<Product>> products;
products.push_back(std::make_unique<PackagedProduct>(1, 2, 3));
products.push_back(std::make_unique<FreshProduct>(4, 5, 6));
Instead of checking the dynamic type at runtime and downcast, you should read up on virtual methods.
The basic idea is the same as in Java: Use inheritance to create a class hierarchy:
class Product { public: virtual ~Product(); ... };
class PackagedProduct : public Product { ... };
class FreshProduct : public Product { ... };
In Java, the vector (or list, container, ...) stores by-reference, not by-value. That's the crucial difference. In C++, this means using a smart pointer:
std::vector< std::shared_ptr< Product > > v;
v.push_back( std::make_shared< FreshProduct >( some args... ) );
You can use dynamic_pointer_cast once you retrieved the pointer back from the vector to check what object it is, but other options are available.
This is, of course, just a rough idea and you'll need to learn a lot about the details, shared_ptr, etc. but I hope you have enough keywords and ideas to google now :)
Create a Product object with the common fields and a PackagedProduct (extending Product) plus a FreshProduct (also extending Product) with their particular fields. Then store smart pointers to them in:
std::vector<std::unique_ptr<Product> > Vec;
If FredOverflow's suggestion of using two vectors doesn't suit your needs, an alternative could be to make a variant of the two types and keep a vector of variants. This is fairly easy to do using boost::variant. You can wrap the functionality you need in free functions or you can wrap the variant in a class. Here's an example
#include <boost/variant/variant.hpp>
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/static_visitor.hpp>
struct FreshProduct
{
double price;
bool needsRefrigeration;
};
struct PackagedProduct
{
double price;
};
struct VariantProduct
{
VariantProduct(const FreshProduct& p) : product(p) {}
VariantProduct(const PackagedProduct& p) : product(p) {}
double getPrice() const;
bool needsRefrigeration() const
{
struct helper : public boost::static_visitor<bool>
{
bool operator ()(const FreshProduct& product) const
{
return product.needsRefrigeration;
}
bool operator ()(...) const
{
return false;
}
};
return boost::apply_visitor(helper(), product);
}
private:
boost::variant<FreshProduct, PackagedProduct> product;
};
// in cpp
namespace {
struct GetPrice : public boost::static_visitor<double>
{
template <class T>
double operator ()(const T& product) const
{
return product.price;
}
};
} // anonymous namespace
double VariantProduct::getPrice() const
{
return boost::apply_visitor(GetPrice(), product);
}
The advantage of this approach is that you don't need to use dynamic allocation in order to keep a collection of fresh or packaged products. The downside is that it is not so easy to extend the types supported in the variant as when using inheritance.
My goal is to create a 2D array class with dense or sparse storage, with support for sub-matrices.
I want to be able to have either dense storage ( std::vector) or sparse ( std::map )
I also want to be able to have sub-matrices that pretty much use the same get/set interface
Mock classes below:
struct ArrayBox
{
int w,h,x,y;
bool in_range(...)
};
template<class T>
struct DenseArray2D
{
ArrayBox box;
std::vector<T> data;
T& get(x,y)
// etc
}
template<class T>
struct SparseArray2D
{
ArrayBox box;
std::map<size_t, T> data;
T& get(x,y)
// etc, interface identical to above
}
Now, how can I add support for subarrays into this so that I write as little identical code as possible? The only differenceof the subarray is that it has a reference to the data (given in the ctor) and a different box
The above design is of course not set in stone, so any suggestions welcome.
I've been trying to find a way to sample random vectors from a multivariate normal distribution in C++, having both the mean vector and the covariance matrix, much like Matlab's mvnrnd function works. I've found relevant code for a class that implements this on this page, but I've been having some problems compiling it. I've created a header file that is being included on my main.cpp, and I'm trying to create an object of the EigenMultivariateNormal class:
MatrixXd MN(10,1);
MatrixXd CVM(10,10);
EigenMultivariateNormal <double,int> (&MN,&CVM) mvn;
The problem is I get a template-related error when compiling:
error: type/value mismatch at argument 2 in template parameter list for ‘template<class _Scalar, int _size> class EigenMultivariateNormal’
error: expected a constant of type ‘int’, got ‘int’
error: expected ‘;’ before ‘mvn’
I only have a superficial idea on how to work with templates, and I am by no means a cpp expert, so I was wondering what exactly am I doing wrong? Apparently I should have a const somewhere on my code.
That code's a bit old. Here's a newer, possibly improved version. There are probably still some bad things. For example, I think it should be changed to use the MatrixBase instead of an actual Matrix. That might let it optimize and better decide when it needs to allocate storage space or not. This also uses the namespace internal which is probably frowned on, but it seems necessary to make use of Eigen's NullaryExpr which seems like the right thing to do. There's usage of the dreaded mutable keyword. That's necessary because of what Eigen thinks should be const when used in a NullaryExpr.
It's also a little annoying to rely on boost. It seems that in C++11 the necessary functions have become standard. Below the class code, there's a short usage sample.
The class eigenmultivariatenormal.hpp
#ifndef __EIGENMULTIVARIATENORMAL_HPP
#define __EIGENMULTIVARIATENORMAL_HPP
#include <Eigen/Dense>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/normal_distribution.hpp>
/*
We need a functor that can pretend it's const,
but to be a good random number generator
it needs mutable state. The standard Eigen function
Random() just calls rand(), which changes a global
variable.
*/
namespace Eigen {
namespace internal {
template<typename Scalar>
struct scalar_normal_dist_op
{
static boost::mt19937 rng; // The uniform pseudo-random algorithm
mutable boost::normal_distribution<Scalar> norm; // The gaussian combinator
EIGEN_EMPTY_STRUCT_CTOR(scalar_normal_dist_op)
template<typename Index>
inline const Scalar operator() (Index, Index = 0) const { return norm(rng); }
};
template<typename Scalar>
boost::mt19937 scalar_normal_dist_op<Scalar>::rng;
template<typename Scalar>
struct functor_traits<scalar_normal_dist_op<Scalar> >
{ enum { Cost = 50 * NumTraits<Scalar>::MulCost, PacketAccess = false, IsRepeatable = false }; };
} // end namespace internal
/**
Find the eigen-decomposition of the covariance matrix
and then store it for sampling from a multi-variate normal
*/
template<typename Scalar, int Size>
class EigenMultivariateNormal
{
Matrix<Scalar,Size,Size> _covar;
Matrix<Scalar,Size,Size> _transform;
Matrix< Scalar, Size, 1> _mean;
internal::scalar_normal_dist_op<Scalar> randN; // Gaussian functor
public:
EigenMultivariateNormal(const Matrix<Scalar,Size,1>& mean,const Matrix<Scalar,Size,Size>& covar)
{
setMean(mean);
setCovar(covar);
}
void setMean(const Matrix<Scalar,Size,1>& mean) { _mean = mean; }
void setCovar(const Matrix<Scalar,Size,Size>& covar)
{
_covar = covar;
// Assuming that we'll be using this repeatedly,
// compute the transformation matrix that will
// be applied to unit-variance independent normals
/*
Eigen::LDLT<Eigen::Matrix<Scalar,Size,Size> > cholSolver(_covar);
// We can only use the cholesky decomposition if
// the covariance matrix is symmetric, pos-definite.
// But a covariance matrix might be pos-semi-definite.
// In that case, we'll go to an EigenSolver
if (cholSolver.info()==Eigen::Success) {
// Use cholesky solver
_transform = cholSolver.matrixL();
} else {*/
SelfAdjointEigenSolver<Matrix<Scalar,Size,Size> > eigenSolver(_covar);
_transform = eigenSolver.eigenvectors()*eigenSolver.eigenvalues().cwiseMax(0).cwiseSqrt().asDiagonal();
/*}*/
}
/// Draw nn samples from the gaussian and return them
/// as columns in a Size by nn matrix
Matrix<Scalar,Size,-1> samples(int nn)
{
return (_transform * Matrix<Scalar,Size,-1>::NullaryExpr(Size,nn,randN)).colwise() + _mean;
}
}; // end class EigenMultivariateNormal
} // end namespace Eigen
#endif
Here's a simple program that uses it:
#include <fstream>
#include "eigenmultivariatenormal.hpp"
#ifndef M_PI
#define M_PI REAL(3.1415926535897932384626433832795029)
#endif
/**
Take a pair of un-correlated variances.
Create a covariance matrix by correlating
them, sandwiching them in a rotation matrix.
*/
Eigen::Matrix2d genCovar(double v0,double v1,double theta)
{
Eigen::Matrix2d rot = Eigen::Rotation2Dd(theta).matrix();
return rot*Eigen::DiagonalMatrix<double,2,2>(v0,v1)*rot.transpose();
}
void main()
{
Eigen::Vector2d mean;
Eigen::Matrix2d covar;
mean << -1,0.5; // Set the mean
// Create a covariance matrix
// Much wider than it is tall
// and rotated clockwise by a bit
covar = genCovar(3,0.1,M_PI/5.0);
// Create a bivariate gaussian distribution of doubles.
// with our chosen mean and covariance
Eigen::EigenMultivariateNormal<double,2> normX(mean,covar);
std::ofstream file("samples.txt");
// Generate some samples and write them out to file
// for plotting
file << normX.samples(1000).transpose() << std::endl;
}
And here's a plot showing the results.
Using the SelfAdjointEigenSolver is probably a lot slower than a Cholesky decomposition, but it is stable, even if the covariance matrix is singular. If you know that your covariance matrices will always be full, then you could use that instead. However, if you create the distribution rarely and sample from it often, then that's probably not a big deal.
template<class _Scalar, int _size> class EigenMultivariateNormal is specialized template class. The first class _Scalar ask for a type but int _size ask for an int.
You should call it with a constant int instead of the type int as you did.
Secondly, your syntax to instance a new class EigenMultivariateNormal is wrong.
Try this instead:
EigenMultivariateNormal<double, 10> mvn (&MN, &CVM); // with 10 is the size
Is there a way to have a matrix of user-defined type in OpenCV 2.x? Something like :
cv::Mat_<KalmanRGBPixel> backgroundModel;
I know cv::Mat<> is meant for image and mathematic, but I want to hold data in a matrix form. I don't plan to use inverse, transpose, multiplication, etc., it's only to store data. I want it to be in matrix form because the pixel_ij of each frame of a video will be linked to backgroundModel_ij.
I know there is a DataType<_Tp> class in core.hpp that needs to be defined for my type but I'm not sure how to do it.
EDIT : KalmanRGBPixel is only a wrapper for cv::KalmanFilter class. As for now, it's the only member.
... some functions ...
private:
cv::KalmanFilter kalman;
Thanks for your help.
I have a more long winded answer for anybody wanting to create a matrix of custom objects, of whatever size.
You will need to specialize the DataType template but instead of having 1 channel, you make the channels the same size of your custom object. You may also need to override a few functions to get expected functionality, but back to that later.
First, here is an example of my custom type template specialization:
typedef HOGFilter::Sample Sample;
namespace cv {
template<> class DataType<Sample>
{
public:
typedef HOGFilter::Sample value_type;
typedef HOGFilter::Sample channel_type;
typedef HOGFilter::Sample work_type;
typedef HOGFilter::Sample vec_type;
enum {
depth = CV_8U,
channels = sizeof(HOGFilter::Sample),
type = CV_MAKETYPE(depth, channels),
};
};
}
Second.. you may want to override some functions to get expected functionality:
// Special version of Mat, a matrix of Samples. Using the power of opencvs
// matrix manipulation and multi-threading capabilities
class SampleMat : public cv::Mat_<Sample>
{
typedef cv::Mat_<Sample> super;
public:
SampleMat(int width = 0, int height = 0);
SampleMat &operator=(const SampleMat &mat);
const Sample& at(int x, int y = 0);
};
The typedef of super isnt required but helps with readability in the cpp.
Notice I have overriden the constructor with width/hight parameters. This is because we have to instantiate the mat this way if we want a 2D matrix.
SampleMat::SampleMat(int width, int height)
{
int count = width * height;
for (int i = 0; i < count; ++i)
{
HOGFilter::Sample sample;
this->push_back(sample);
}
*dynamic_cast<Mat_*>(this) = super::reshape(channels(), height);
}
The at<_T>() override is just for cleaner code:
const Sample & SampleMat::at(int x, int y)
{
if (y == 0)
return super::at<Sample>(x);
return super::at<Sample>(cv::Point(x, y));
}
In the OpenCV documentation it is explained how to add custom types to OpenCV matrices. You need to define the corresponding cv::DataType.
https://docs.opencv.org/master/d0/d3a/classcv_1_1DataType.html
The DataType class is basically used to provide a description of such primitive data types without adding any fields or methods to the corresponding classes (and it is actually impossible to add anything to primitive C/C++ data types). This technique is known in C++ as class traits. It is not DataType itself that is used but its specialized versions […] The main purpose of this class is to convert compilation-time type information to an OpenCV-compatible data type identifier […]
(Yes, finally I answer the question itself in this thread!)
If you don't want to use the OpenCV functionality, then Mat is not the right type for you.
Use std::vector<std::vector<Type> > instead. You can give the size during initialization:
std::vector<std::vector<Type> > matrix(42, std::vector<Type>(23));
Then you can access with []-operator. No need to screw around with obscure cv::Mats here.
If you would really need to go for an OpenCV-Matrix, you are right in that you have to define the DataType. It is basically a bunch of traits. You can read about C++ Traits on the web.
You can create a CV mat that users your own allocated memory by specifying the address to the constructor. If you also want the width and height to be correct you will need to find an openCV pixel type that is the same number of bytes.
I have searched the web but could not find an answer.
how do I have set base index in the matrix, such that indexes start from values other than zero? for example:
A(-3:1) // Matlab/fortran equivalent
A.reindex(-3); // boost multi-array equivalent
thanks
Your search appears to be correct; it appears not to have such a function.
If you really need this functionality, perhaps you could consider subclassing the matrix and overriding the operator() to fiddle with the indices for you. For example:
using namespace boost::numeric::ublas;
template<typename T>
class Reindexable : public matrix<T>
{
public:
Reindexable() : m_offset(0) {}
void reindex(int offset) { m_offset = offset; }
T& operator()(int i) { return matrix<T>::operator()(i + m_offset); }
/* Probably more implementation needed here ... */
private:
int m_offset;
}
I've been programming in VB.NET (ughh!) and C# lately, so I'm a little rusty on my C++ syntax and have probably made a few mistakes in the above, but the general idea should work. You subclass the matrix so that you can provide a reindex operation and override the parenthesis operator so that it is aware of the new index offset. Of course, in the actual implementation, you will need offsets for each dimension of the matrix.
Also, if you ever have a reference or pointer to your Reindexable, and the type of the reference/pointer is matrix<T>, then you will be using the old index operator, so be careful!