Eigen Assertion Error when Converting from MatrixXf to ArrayXf - c++

I wrote a simple program that adds a scalar to a Random matrix.
#include <Eigen/Dense>
int main() {
Eigen::MatrixXf mat = Eigen::MatrixXf::Random(100, 100);
Eigen::ArrayXf arr = mat.array() + 1;
}
The program compiles without any errors. However, when executing this line:
Eigen::ArrayXf arr = mat.array() + 1;
I get the following error:
Assertion failed: (other.rows() == 1 || other.cols() == 1), function resizeLike, file /usr/local/Cellar/eigen/3.3.7/include/eigen3/Eigen/src/Core/PlainObjectBase.h, line 374.
I read the official documentation (https://eigen.tuxfamily.org/dox/group__TutorialArrayClass.html) and I don't understand what I am doing wrong.

It's been a while since I did anything with Eigen, but I believe you can't mix Arrays and Matrices like that.
However, it seems like you're generating a 100x100 matrix with random numbers, and I'm not sure why it trips up. I don't have Eigen readily available at the moment, but changing it to use fixed size arrays seems to be working on Godbolt.
// Type your code here, or load an example.
#include <iostream>
#include <Eigen/Dense>
int main()
{
Eigen::Matrix<float, 100, 100> mat = Eigen::Matrix<float, 100, 100>::Random();
Eigen::Array<float, 100, 100> arr = mat.array() + 1;
std::cout << "Mat 0,0: " << mat(0,0) << "\nArr 0,0: " << arr(0,0) << "\n";
std::cout << "Mat 2,0: " << mat(2,0) << "\nArr 2,0: " << arr(2,0);
return 0;
}
ASM generation compiler returned: 0
Execution build compiler returned: 0
Program returned: 0
Mat 0,0: 0.680375
Arr 0,0: 1.68038
Mat 2,0: 0.566198
Arr 2,0: 1.5662
https://godbolt.org/z/9xu5oW

Solved it. Apparently, I had to store the result of the addition in a MatrixXf, not in an ArrayXf.
#include <Eigen/Dense>
int main() {
Eigen::MatrixXf mat = Eigen::MatrixXf::Random(100, 100);
Eigen::MatrixXf arr = mat.array() + 1;
}

The corresponding Array object to MatrixXf (aka, Matrix<float, Dynamic, Dynamic>) would be ArrayXXf (aka, Array<float, Dynamic, Dynamic>). ArrayXf is a typedef for Array<float, Dynamic, 1>, similar to a VectorXf (aka, Matrix<float, Dynamic, 1>).
The following should work as expected:
#include <Eigen/Core>
int main() {
Eigen::MatrixXf mat = Eigen::MatrixXf::Random(100, 100);
Eigen::ArrayXXf arr = mat.array() + 1;
}

Related

How to get the rows based on condition in eigen?

I need to get the certain rows, when a element is a vector is one.
For an example:
std::vector<bool>index{}; //contains 6000 numbers of elements 0 and 1
Now I have a matrix mat of shape (6000,4)
How can I get the rows in a matrix mat, when the corresponding element is 1 in vector index.
mat = mat[index];
If I understand your question clearly, you may find good answer from this good reply:
Eigen3 select rows out based on column conditions
Using new feature (Eigen 3.4 or 3.3.90 development branch) and take the core code from the previous link:
#include <Eigen/Dense>
#include <iostream>
#include <vector>
using namespace Eigen;
int main() {
MatrixXd mat = MatrixXd::Random(10,5);
std::cout << "original:\n" << mat << std::endl;
std::vector<int> keep_rows;
for (int i = 0; i < mat.rows(); ++i) {
if (mat(i,mat.cols() - 1) > 0.3) {
keep_rows.push_back(i);
}
}
VectorXi keep_cols = VectorXi::LinSpaced(mat.cols(), 0,mat.cols());
MatrixXd mat_sel = mat(keep_rows, keep_cols);
std::cout << "selected:\n" << mat_sel << std::endl;
}
It uses the similar style of the Matlab:
MatrixXd mat_sel = mat(keep_rows, keep_cols);
But the columns and rows that should be kept are stored in an
Eigen::VectorXi
or in a
std::vector<int>

opencv FLANN radiusSearch give bad results

I'd like to do radius search to find all valid neighbors, but it seems to give me wrong results. Here is my code
#include "opencv/cv.hpp"
#include <iostream>
#include <vector>
int main () {
// create a group of points
std::vector<cv::Point2f> points;
points.emplace_back(438.6, 268.8);
points.emplace_back(439.1, 268.6);
points.emplace_back(438.2, 268.1);
points.emplace_back(498.3, 285.9);
points.emplace_back(312.9, 245.9);
points.emplace_back(313.4, 245.7);
points.emplace_back(313.1, 245.5);
points.emplace_back(312.5, 245.4);
points.emplace_back(297.6, 388.1);
points.emplace_back(291.7, 309.8);
points.emplace_back(194.1, 369.8);
points.emplace_back(439.9, 314.9);
points.emplace_back(312.8, 246.0);
// create features array
cv::Mat_<float> features(0, 2);
for (auto && point : points) {
//Fill matrix
cv::Mat row = (cv::Mat_<float>(1, 2) << point.x, point.y);
features.push_back(row);
}
std::cout << features << std::endl;
cv::flann::Index flann_index(features, cv::flann::KDTreeIndexParams());
std::vector<float> query{ 300.6f, 268.8f };
std::vector<int> ind;
std::vector<float> d;
unsigned int max_neighbours = 10;
// Here I deliberately increase the radius to contain all the points
double radius = 500.0;
flann_index.radiusSearch(query, ind, d, radius, max_neighbours,
cv::flann::SearchParams());
}
Output of ind is [0,0,0,0,0,0,0,0,0,0], all zeros, which is unexpected.
Anyone knows why?
=-=-=-=-=-=-=-=-=-=-= Update
int main() {
// create a group of points
std::vector<cv::Point2f> points;
points.emplace_back(438.6, 268.8);
points.emplace_back(439.1, 268.6);
points.emplace_back(438.2, 268.1);
points.emplace_back(498.3, 285.9);
points.emplace_back(312.9, 245.9);
points.emplace_back(313.4, 245.7);
points.emplace_back(313.1, 245.5);
points.emplace_back(312.5, 245.4);
points.emplace_back(297.6, 388.1);
points.emplace_back(291.7, 309.8);
points.emplace_back(194.1, 369.8);
points.emplace_back(439.9, 314.9);
points.emplace_back(312.8, 246.0);
// create features array
cv::Mat_<float> features(0, 2);
for (auto && point : points) {
//Fill matrix
cv::Mat row = (cv::Mat_<float>(1, 2) << point.x, point.y);
features.push_back(row);
}
std::cout << features << std::endl;
cv::flann::GenericIndex<cvflann::L2<float> > index(features, cvflann::KDTreeIndexParams());
std::vector<float> query(438.6f, 268.8f);
std::vector<int> ind;
std::vector<float> d;
index.radiusSearch(query, ind, d, 45.f, cvflann::SearchParams());
// I can print std::vector by some method, the reader may not, so I comment this line
//std::cout << d << std::endl;
}
As cv::flann::Index is deprecated, I change to new API, but this time, the program just doesn't work anymore.
If you check the example of the plain FLANN I had used here, you would see that they call buildIndex(), which you don't. Could that be?
Try:
cv::flann::Index flann_index(features, cv::flann::KDTreeIndexParams());
flann_index.buildIndex();
You have to set the size of ind and d.
I encountered this issue, and the solution is that radius must be specified as radius squared (^2). And the length of ind and d will be set by max_neighbors, but the return of radiusSearch must be checked to find out which is less, num_found or max_neighbours;
double radius = 500.0;
int num_found = flann_index.radiusSearch(query, ind, d, radius*radius, max_neighbours, cv::flann::SearchParams());
num_found = min(num_found,(int)ind.size()); // check correct size
for(int i=0; i<num_found; i++) { ... ind[i] ... d[i] ... }
in my case, i also had to adjust the index and search parameters to return correct results:
flann::KDTreeIndexParams indexParams(128);
flann::SearchParams searchParams(1024,0.0,true);

C++ Eigen - How to combine broadcasting and elementwise operations

I have a MatrixXf variable and a VectorXf variable. I would like to perform a rowwise division using the Vector on my Matrix. Is it possible to do something like this?
#include <iostream>
#include "Eigen/Dense"
using namespace std;
using namespace Eigen;
int main() {
MatrixXf mat(3, 2);
mat << 1, 2,
3, 4,
5, 6;
VectorXf vec(2);
vec << 2, 3;
mat = mat.rowwise() / vec;
cout << mat << endl;
return 0;
}
I am expecting to get a matrix with value [0.5, 0.667; 1.5, 1.333; 2.5, 2].
Thank you very much!
You need to use the matrix and vector as arrays (and not linear algebra objects, see docs). To do so, you would rewrite the relevant line as:
mat = mat.array().rowwise() / vec.transpose().array();
cout << mat << endl; // Note that in the original this was vec
The transpose is needed as the VectorXf is a column vector by definition, and you wanted a row vector.

copying a static array to dynamic array

I am defining a dynamic array in c++:
double *values;
int size = 5;
values = new (nothrow) double[size];
I know this works because it compiles, but I see some potential problems.
Say I want to assign values to this array:
double samples = [1,2,3,4,5,6];
values = samples; //runtime error: free or corruption
What exactly is happening to generate this error?
You should use std::copy to copy a static array to a dynamic array like the example below:
#include <iostream>
#include <algorithm>
int main() {
int *a = new int[5];
int b[] = {1, 2, 3, 4, 5};
std::copy(b, b + 5, a);
for(std::size_t i(0); i < 5; ++i) std::cout << a[i] << " ";
std::cout << std::endl;
return 0;
}
LIVE DEMO
Alternatively if you want the convenience of assignments instead of element-wise copying and provided that you know the size of the arrays in compile time and your compiler supports C++11 features, use std::arrays like the example below:
#include <iostream>
#include <array>
int main() {
std::array<int, 5> a;
std::array<int, 5> b {{1, 2, 3, 4, 5}};
a = b;
for(auto i : a) std::cout << i << " ";
std::cout << std::endl;
return 0;
}
LIVE DEMO
However, it is recommend to prefer std::vector over the use of raw dynamic arrays like the example below:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> a(5);
int b[] = {1, 2, 3, 4, 5};
std::copy(b, b + 5, a.begin());
for(auto i : a) std::cout << i << " ";
std::cout << std::endl;
return 0;
}
LIVE DEMO
It doesn't work because you're assigning a static array to a pointer.
double *values;
double samples[] = {1,2,3,4,5,6};
They're two different data types as far as the compiler is concerned.
When you say:
values = new double[size];
You're creating a block of heap (dynamic) memory, and "values" holds the memory address of the first element in the array. To fill in the values from your static array, you need to assign each element individually like so:
values[0] = samples[0];
values[1] = samples[1];
// or better yet
for (int i = 0; i < size; i++)
values[i] = samples[i]
You can use a std::vector which has an iterator constructor, this will solve the problem for you.
std::vector<double> values(std::begin(samples), std::end(samples));
This will ensure the heap memory is properly cleaned up, even in the case of exception, and employs the implementation's debugging machinery to help protect you from events like buffer overflow.

Extract a block from a sparse matrix as another sparse matric

How to extract a block from a Eigen::SparseMatrix<double>. It seems there aren't the methods I used for the dense ones.
‘class Eigen::SparseMatrix<double>’ has no member named ‘topLeftCorner’
‘class Eigen::SparseMatrix<double>’ has no member named ‘block’
There is a way to extract a block as a Eigen::SparseMatrix<double> ?
I made this function to extract blocks from a Eigen::SparseMatrix<double,ColMaior>
typedef Triplet<double> Tri;
SparseMatrix<double> sparseBlock(SparseMatrix<double,ColMajor> M,
int ibegin, int jbegin, int icount, int jcount){
//only for ColMajor Sparse Matrix
assert(ibegin+icount <= M.rows());
assert(jbegin+jcount <= M.cols());
int Mj,Mi,i,j,currOuterIndex,nextOuterIndex;
vector<Tri> tripletList;
tripletList.reserve(M.nonZeros());
for(j=0; j<jcount; j++){
Mj=j+jbegin;
currOuterIndex = M.outerIndexPtr()[Mj];
nextOuterIndex = M.outerIndexPtr()[Mj+1];
for(int a = currOuterIndex; a<nextOuterIndex; a++){
Mi=M.innerIndexPtr()[a];
if(Mi < ibegin) continue;
if(Mi >= ibegin + icount) break;
i=Mi-ibegin;
tripletList.push_back(Tri(i,j,M.valuePtr()[a]));
}
}
SparseMatrix<double> matS(icount,jcount);
matS.setFromTriplets(tripletList.begin(), tripletList.end());
return matS;
}
And these if the sub-matrix is in one of the four corners:
SparseMatrix<double> sparseTopLeftBlock(SparseMatrix<double> M,
int icount, int jcount){
return sparseBlock(M,0,0,icount,jcount);
}
SparseMatrix<double> sparseTopRightBlock(SparseMatrix<double> M,
int icount, int jcount){
return sparseBlock(M,0,M.cols()-jcount,icount,jcount);
}
SparseMatrix<double> sparseBottomLeftBlock(SparseMatrix<double> M,
int icount, int jcount){
return sparseBlock(M,M.rows()-icount,0,icount,jcount);
}
SparseMatrix<double> sparseBottomRightBlock(SparseMatrix<double> M,
int icount, int jcount){
return sparseBlock(M,M.rows()-icount,M.cols()-jcount,icount,jcount);
}
This is now supported in Eigen 3.2.2 Docs (though maybe earlier versions support it too).
#include <iostream>
#include <Eigen/Dense>
#include <Eigen/Sparse>
using namespace Eigen;
int main()
{
MatrixXd silly(6, 3);
silly << 0, 1, 2,
0, 3, 0,
2, 0, 0,
3, 2, 1,
0, 1, 0,
2, 0, 0;
SparseMatrix<double, RowMajor> sparse_silly = silly.sparseView();
std::cout <<"Whole Matrix" << std::endl;
std::cout << sparse_silly << std::endl;
std::cout << "block of matrix" << std::endl;
std::cout << sparse_silly.block(1,1,3,2) << std::endl;
return 0;
}
There is very sparse support (sorry, no pun intended) for submatrices in sparse matrices. Effectively you can only access a continuous set of rows for row-major, and columns for column major. The reason for that is not that the matrices could be empty, but rather that the indexing scheme is somewhat more complicated than with dense matrices. With dense matrices you only need an additional stride number in order to support sub-matrix support.