I have implemented this code with Eigen library to have Triplet structure. This code works very well in my project on my Mac OS X. However the same code don't work on Linux platform.
Eigen::SparseMatrix<double> spdiags(const MatrixXd& B, const
Eigen::Matrix<int, 1,1>& d, size_t m, size_t n)
{
Eigen::SparseMatrix<double> A(m,n);
typedef Eigen::Triplet<double> T;
std::vector<T> triplets;
triplets.reserve(std::min(m,n)*d.size());
for (int k = 0; k < d.size(); k++)
{
int i_min = std::max(0, -d(k));
int i_max = std::min(m - 1, n - d(k) - 1);
int B_idx_start = m >= n ? d(k) : 0;
for (int i = i_min; i <= i_max; i++) {
triplets.push_back( T(i, i+k, B(B_idx_start + i, k)) );
}
A.setFromTriplets(triplets.begin(), triplets.end());
std::cout << "Row\tCol\tVal" <<std::endl;
for (int k=0; k < A.outerSize(); ++k)
{
for (SparseMatrix<double>::InnerIterator it(A,k); it; ++it)
{
std::cout << it.row() << "\t"; // row index
std::cout << it.col() << "\t";
std::cout << it.value() << std::endl;
}
}
return A;
}
I have this error only on Linux (there is no error on Mac). The code source of the file DenseCoeffsBase.h
is the same:
"/usr/local/include/Eigen/src/Core/DenseCoeffsBase.h:114:
Eigen::DenseCoeffsBase<Derived, 0>::CoeffReturnType
Eigen::DenseCoeffsBase<Derived, 0>::operator()
(Eigen::DenseCoeffsBase<Derived, 0>::Index,
Eigen::DenseCoeffsBase<Derived, 0>::Index) const
[with Derived = Eigen::Matrix<double, -1, -1>;
Eigen::DenseCoeffsBase<Derived, 0>::CoeffReturnType = const double&;
Eigen::DenseCoeffsBase<Derived, 0>::Index = long int]:
Assertion `row >= 0 && row < rows() && col >= 0 && col < cols()' failed."
Any ideas?
Here is an MVC as asked :
#include<Eigen/Sparse>
#include <Eigen/Sparse>
#include<Eigen/Dense>
#include<Eigen/Eigenvalues>
Matrix<int, 1, 1> d1; d1(0)=0;
MatrixXd d0; d0.resize(1,5);
d0(0)=10;d0(1)=20;d0(2)=30;d0(3)=30;d0(4)=40;d0(5)=50;
Eigen::SparseMatrix<double> Diag_laplacian=test.spdiags(d0,d1,5,5);
//--------------
//the result must be like this :
Row Col Val
0 0 10
1 1 20
2 2 30
3 3 30
4 4 40
This, my dear sir/madam, is an MCVE
#include <iostream>
#include <Eigen/Core>
#include <Eigen/Sparse>
using namespace Eigen;
Eigen::SparseMatrix<double> spdiags(const MatrixXd& B,
const Eigen::Matrix<int, 1, 1>& d, size_t m, size_t n)
{
Eigen::SparseMatrix<double> A(m, n);
typedef Eigen::Triplet<double> T;
std::vector<T> triplets;
triplets.reserve(std::min(m, n)*d.size());
for (int k = 0; k < d.size(); k++)
{
int i_min = std::max(0, -d(k));
int i_max = std::min(m - 1, n - d(k) - 1);
int B_idx_start = m >= n ? d(k) : 0;
for (int i = i_min; i <= i_max; i++)
triplets.push_back(T(i, i + k, B(B_idx_start + i, k)));
}
A.setFromTriplets(triplets.begin(), triplets.end());
std::cout << "Row\tCol\tVal" << std::endl;
for (int k = 0; k < A.outerSize(); ++k)
{
for (SparseMatrix<double>::InnerIterator it(A, k); it; ++it)
{
std::cout << it.row() << "\t"; // row index
std::cout << it.col() << "\t";
std::cout << it.value() << std::endl;
}
}
return A;
}
int main()
{
Matrix<int, 1, 1> d1; d1(0) = 0;
MatrixXd d0; d0.resize(1, 5);
// Note that you *have* to use (x,y) indices on a MatrixXd
// Otherwise, you get a different assertion failure
d0(0,0) = 10; d0(0,1) = 20;
d0(0,2) = 30; d0(0,3) = 30;
d0(0,4) = 40;
// d0(0,5) = 50; // OUT OF BOUNDS!!!
Eigen::SparseMatrix<double> Diag_laplacian = spdiags(d0, d1, 5, 5);
}
The expected result is (as you stated):
Row Col Val
0 0 10
1 1 20
2 2 30
3 3 30
4 4 40
To reproduce the results, I can either use VS (2013 in my case) or g++ (i.e. it's not Linux vs. Mac). As you are using g++, I will as well.
To reproduce the behavior you described on the Linux build, I compiled with
g++ -O3 -I"C:\usr\include" Source.cpp -o a.exe
Running a.exe
gave me (as you stated)
Assertion failed: row >= 0 && row < rows() && col >= 0 && col < cols(), file C:\usr\include/Eigen/src/Core/DenseCoeffsBase.h, line 114
Debugging it showed me that it fails on the line
triplets.push_back(T(i, i + k, B(B_idx_start + i, k)));
when i == 1
. Why? Exactly as @marc and I stated. B
is not shaped/sized as you use it. Changing B(B_idx_start + i, k)
with B(k, B_idx_start + i)
resolves the issue.
Now, why does it work on the Mac? The answer has to do with the error itself. It's an assertion error. Assertions are not checked when NDEBUG
is defined. So you probably compiled using something like
g++ -DNDEBUG -O3 -I"C:\usr\include" Source.cpp -o a.exe
on the Mac, and it ran fine, as then the assertions are ignored:
#ifdef NDEBUG
#define assert(_Expression) ((void)0)
#else
So, if there is an assertion failure, why does it work when we define NDEBUG
? The answer to that is that the data pointer points to the first of five allocated doubles
. Using the correct indexing, we should get index = k*1 + (B_idx_start + i)
, and since in this case k==0
and B_idx_start==0
, we get index=i
. This is within the bounds and therefore we don't get an out of bounds exception. Using the incorrect indexing, we get index = (B_idx_start + i)*1 + k
which again, results in index=i
. If the size of the matrix was (for example) 2x5, then we would have gotten an out of bounds exception.