I need a 3D matrix/array structure on my code, and right now I'm relying on Eigen for both my matrices and vectors.
Right now I am creating a 3D structure using new:
MatrixXd* cube= new MatrixXd[60];
for (int i; i<60; i++) cube[i]=MatrixXd(60,60);
and for acessing the values:
double val;
MatrixXd pos;
for (int i; i<60; i++){
pos=cube[i];
for (int j; j<60; j++){
for (int k; k<60; k++){
val=pos(j,k);
//...
}
}
}
However, right now it is very slow in this part of the code, which makes me beleive that this might not be the most efficient way. Are there any alternatives?
While it was not available, when the question was asked, Eigen has been providing a Tensor module for a while now. It is still in an "unsupported" stage (meaning the API may change), but basic functionality should be mostly stable. The documentation is scattered here and here.
A solution I used is to form a fat matrix containing all the matrices you need stacked.
MatrixXd A(60*60,60);
and then access them with block operations
A0 = A.block<60,60>(0*60,0);
...
A5 = A.block<60,60>(5*60,0);
An alternative is to create a very large chunk of memory ones, and maps Eigen matrices from it:
double* data = new double(60*60 * 60*60*60);
Map<MatrixXd> Mijk(data+60*(60*(60*k)+j)+i), 60, 60);
At this stage you can use Mijk like a MatrixXd object. However, since this not a MatrixXd type, if you want to pass it to a function, your function must either:
be of the form foo(Map<MatrixXd> mat)
be a template function: template<typename Der> void foo(const MatrixBase<Der>& mat)
take a Ref<MatrixXd> object which can handle both Map<> and Matrix<> objects without being a template function and without copies. (doc)
Related
I'm using Stroustrup's matrix.h implementation as I have a lot of matrix heavy computation to do. It will make life easier if I can just get the matrix populated!
I'm receiving a complex object with a matrix that is not known until received. Once it enters the method, I can get the row and column count, but I have to use a double i,j loop to pull the values since they are in a cpp17::any structure and I have to convert them using asNumber().
I declare it as follows as part of an object definition:
Matrix<double,2> inputLayer;
In the code that instantiates the object, I have the following code:
int numRows = sourceSeries->rowCount();
int numColumns = sourceSeries->columnCount();
int i,j = 0;
for(i=0; i<numRows; i++){
for(j=0;j<numColumns;j++) {
// make sure you skip the header row in sourceSeries
inputLayer[i][j] = asNumber(sourceSeries->data(i+1,j,ItemDataRole::Display));
}
}
There is nothing like push_back() for the matrix template. The examples I can find in his books and on the web either pre-populate the matrix in the definition or create it from existing lists, which I won't have at this particular time.
Do I need to define a "new" double, receive the asNumber(), then set inputlayer[][] = the "new" double?
I'm hoping not to have to manage the memory like I can do with vectors that release when I go out of scope, which is why I was avoiding "new."
I'm using the boost frameworks as well and I'm wondering if I should try ublas version instead, or just get this one working.
Thanks for the pointers to Eigen, that was so simple! Here's all I had to do:
In the header file:
#include "Eigen/Dense"
using namespace Eigen;
In the object definition of the header file:
Matrix<double, Dynamic, Dynamic> inputLayer;
In the code where I need to read in the matrix:
int numRows = sourceSeries->rowCount();
int numColumns = sourceSeries->columnCount();
int i,j = 0;
MatrixXd inputLayer(numRows,numColumns);
for(i=0; i<numRows; i++){
for(j=0;j<numColumns;j++) {
// make sure you skip the header row in sourceSeries
inputLayer(i,j) = asNumber(sourceSeries->data(i+1,j,ItemDataRole::Display));
}
}
Sorry I had to waste so much time trying to get the other code to work, but at least I got real familiar with my debugger and the codebase again. Thanks everyone for the comments!
I am pretty competent with Python, but I'm pretty new to C++ and things like pointers. I try to write some codes for solving ODE with the Eigen package for linear algebra (I will need to deal with lots of matrices later, so I plan to start with it). I have the following code for RK4 and they work:
#include "../eigen-eigen-b3f3d4950030/Eigen/Dense"
using namespace Eigen;
VectorXd Func(const VectorXd& a)
{ // equations for solving simple harmonic oscillator
Vector2d ans;
ans(0) = a(1); // dy/dt
ans(1) = -a(0); // d2y/dt2
return ans;
}
MatrixXd RK4(VectorXd Func(const VectorXd& y), const Ref<const VectorXd>& y0, double h, int step_num)
{
MatrixXd y(step_num, y0.rows());
y.row(0) = y0;
for (int i=1; i<step_num; i++){
VectorXd y_old = y.row(i-1).transpose();
VectorXd k1 = h*Func(y_old);
VectorXd k2 = h*Func(y_old+k1/2);
VectorXd k3 = h*Func(y_old+k2/2);
VectorXd k4 = h*Func(y_old+k3);
VectorXd dy = (k1 + 2*k2 + 2*k3 + k4)/6;
y.row(i) = y.row(i-1) + dy.transpose();
}
return y;
}
int main()
{
Vector2d v1;
v1(0) = 1.4; v1(1) = -0.1;
double h = 0.1;
int step_num = 50;
MatrixXd sol = RK4(Func,v1,h,step_num);
return 0;
}
I have the following questions:
What's the meaning of & in the function argument? Pass by reference? I just copied the code from the official documentation, but I'm not too sure if I understand every bit in the function arguments of RK4 such as VectorXd Func(const VectorXd& y). Are there alternative ways of accepting Eigen::MatrixXd and functions which accept Eigen::MatrixXd as function arguments?
From what I understand, we cannot return a whole 2D array from a function, and what we are returning is just the first element of the array (correct me if I'm wrong). What about the Eigen::MatrixX? What are we actually passing/returning? The first element of the matrix, or a completely new object defined by the Eigen library?
I'm not sure if these codes are written efficiently. Are there anything I can do to optimize this part? (Just wondering if I have done anything that may significantly slow down the speed).
Thanks
Yes, & is pass-by-reference; The latter one is syntax for passing a function, that takes a vector by reference and returns a vector. An Eigen::Matrix should always be passed by reference. There are tons of ways to pass one function to another, the most idiomatic ones in C++ are probably template arguments and std::function.
You can't have multiple return arguments, but you can return a pair or a tuple or a Matrix object. RK4 returns a whole matrix.
The code is fairly efficient. If it was really performance-critical there might be a few things that could be optimized, but I would not worry for now.
The biggest point is that RK4 is very general and works with dynamically sized types, which are a lot more expensive than their statically sized counter parts (VectorXf vs Vector2d). But this would require you to create a specialized version for all dimensions you are interested in or to get the compiler to do it for you by using templates.
Generally: Read a good book to get you started.
I am trying to compute colsum(N * P), where N is a sparse, 1M by 2500 matrix, and P is a dense 2500 by 1.5M matrix. I am using the Eigen C++ library with Intel's MKL library. The issue is that the matrix N*P can't actually exist in memory, it's way too big (~10 TB). My question is whether Eigen will be able to handle this computation through some combination of lazy evaluation and parallelism? It says here that Eigen won't make temporary matrices unnecessarily: http://eigen.tuxfamily.org/dox-devel/TopicLazyEvaluation.html
But does Eigen know to compute N * P in piecewise chunks that will actually fit in memory? IE: it will have to do something like colsum(N * P_1) ++ colsum(N * P_2) ++ .. ++ colsum(N * P_n), where P is split into n different submatrices column-wise and "++" is concatenation.
I am working with 128 GB RAM.
I gave it a try but ended up with a bad malloc (I'm only running on 8GB on Win8). I set up my main() and used a not inline colsum function I wrote.
int main(int argc, char *argv[])
{
Eigen::MatrixXd dense = Eigen::MatrixXd::Random(1000, 100000);
Eigen::SparseMatrix<double> sparse(100000, 1000);
typedef Triplet<int> Trip;
std::vector<Trip> trps(dense.rows());
for(int i = 0; i < dense.rows(); i++)
{
trps[i] = Trip(20*i, i, 2);
}
sparse.setFromTriplets(trps.begin(), trps.end());
VectorXd res = colsum(sparse, dense);
std::cout << res;
std::cin >> argc;
return 0;
}
The attempt was simply:
__declspec(noinline) VectorXd
colsum(const Eigen::SparseMatrix<double> &sparse, const Eigen::MatrixXd &dense)
{
return (sparse * dense).colwise().sum();
}
That had a bad malloc. Sol it looks like you have to split it up manually on your own (unless someone else has a better solution).
EDIT
I improved the function a bit, but the get the same bad malloc:
__declspec(noinline) VectorXd
colsum(const Eigen::SparseMatrix<double> &sparse, const Eigen::MatrixXd &dense)
{
return (sparse * dense).topRows(4).colwise().sum();
}
EDIT 2
Another option would be to make the sparse matrix dense and force a lazy evaluation. I don't think that it would work with a sparse matrix (oh well).
__declspec(noinline) VectorXd
colsum(const Eigen::SparseMatrix<double> &sparse, const Eigen::MatrixXd &dense)
{
Eigen::MatrixXd denseSparse(sparse);
return denseSparse.lazyProduct(dense).colwise().sum();
}
This doesn't give me the bad malloc, but computes a lot of pointless 0*x_i expressions.
To answer your question: Especially, when products are involved, Eigen often evaluates parts of expressions into temporaries. In some situations this could be optimized but is not implemented yet, in some cases this is essentially the most efficient way to implement it.
However, in your case you could simply calculate the colsum of N (a 1 x 2500 vector) and multiply that by P.
Maybe future versions of Eigen will be able to make this kind of optimization themselves, but most of the time it is a good idea to make problem-specific optimizations oneself before letting the computer do the rest of the work.
Btw: I'm afraid sparse.colwise() is not implemented yet, so you must compute that manually. If you are lazy, you can instead compute Eigen::RowVectorXd Nsum = Eigen::RowVectorXd::Ones(N.rows())*P; (I have not checked it, but this might actually get optimized to near optimal code, with the most recent versions of Eigen).
I'm creating a circuit analysis library in C++ (also to learn C++, so I'm very new to it).
After getting familiar with Eigen, I'd like to have a matrix where each cell hosts a 3x3 complex matrix.
So far I've tried this very simple prove of principle:
typedef Eigen::MatrixXcd cx_mat;
typedef Eigen::SparseMatrix<cx_mat> sp_mat_mat;
void test(cx_mat Z1){
sp_mat_mat Y(2, 2);
Y(0, 0) = Z1;
Y(2, 2) = Z1;
cout << "\n\nY:\n" << Y << endl;
}
Testing this simple example fails as a probable consequence of Eigen expecting a number instead of a structure.
As a matter of fact the matrix of matrices is prompt to be sparse, hence the sparse matrix structure.
Is there any way to make this work?
Any help is appreciated.
I don't believe Eigen will give you a way to make this work. I you think about the other functions which are connected to Matrix or Sparse matrix, like:
inverse()
norm()
m.row()*m.col()
what should Eigen do when a matrix element number is replaced by a matrix?
What I can understand is that you want to have a data structure that stores your Eigen::MatrixXcd in an memory efficient way.
You could also realize this using the map container:
#include <map>
typedef Eigen::MatrixXcd cx_mat;
cx_mat Z1;
std::map<int,Eigen::MatrixXcd> sp_mat_mat;
int cols = 2;
sp_mat_mat[0*cols+0]=Z1;
sp_mat_mat[2*cols+2]=Z1;
Less memory efficient, but perhaps easier to access would be using the vector container:
#include <vector>
std::vector<std::vector<Eigen::MatrixXcd>> mat_mat;
Have you found a way to create a matrix of matrices?
I see that we can use a 2-D array to create a matrix of matrices.
For example,
#include <Eigen/Dense>
MatrixXd A;
MatrixXd B;
A = MatrixXd::Random(3, 3);
B = MatrixXd::Random(3, 4);
C = MatrixXd::Random(4, 4);
MatrixXd D[2][2];
D[0][0] = A;
D[0][1] = B; D[1][0] = B.transpose();
D[1][1] = C;
I don't know if this way is memory-efficient or not. Let's check it out.
You asked "sparse matrix structure. Is there any way to make this work?" I would say no, because it is not easy to translate a circuit design into a "matrix of matrices" in the first place.. if you want to simulate something, you choose a representation close to it,. In case of an electronic circuit diagram, the schema in memory should IMHO be a directed graph, with linked-list items. At each node/junction, there is a matrix representing the behaviour of a particular component input to output transfer (eg resistor, capacitor, transistor) and you propagate the signal through the matrices assigned to each component. The transformed signal eventually arrives at an output, through the connections in your connected graph. In software, it should work similarly.. Suggested further reading: https://core.ac.uk/download/pdf/53745212.pdf
Is it possible in Eigen to do the equivalent of the following operation in Matlab?
A=rand(10,10);
indices = [2,5,6,8,9];
B=A(indices,indices)
I want to have a submatrix as a view on the original matrix with given, non consecutive indices.
The best option would be to have a shared memory view of the original matrix, is this possible?
I've figured out a method that works but is not very fast, since it involves non vectorized for loops:
MatrixXi slice(const MatrixXi &A, const std::set<int> &indices)
{
int n = indices.size();
Eigen::MatrixXi B;
B.setZero(n,n);
std::set<int>::const_iterator iInd1 = indices.begin();
for (int i=0; i<n;++i)
{
std::set<int>::const_iterator iInd2=indices.begin();
for (int j=0; j<n;++j)
{
B(i,j) = A.coeffRef(*iInd1,*iInd2);
++iInd2;
}
++iInd1;
}
return B;
}
How can this be made faster?
Make your matrix traversal col-major (which is default in Eigen) http://eigen.tuxfamily.org/dox-devel/group__TopicStorageOrders.html
Disable debug asserts, EIGEN_NO_DEBUG, see http://eigen.tuxfamily.org/dox/TopicPreprocessorDirectives.html, as the comment by Deepfreeze suggested.
It is very non-trivial to implement vectorized version since elements are not contiguous in general. If you are up to it, take a look at AVX2 gather instructions (provided you have CPU with AVX2 support)
To implement matrix view (you called it shared-memory) you'd need to implement an Eigen expression, which is not too hard if you are well versed in C++ and know Eigen codebase. I can help you to get started if you so want.