Vector operations on Eigen Array of Vectors - c++

I have a 2D Eigen Array where each item in the array is a 3-element Eigen Vector (e.g. a velocity field over a surface).
I want to multiply each each element of the 2D array with a 3-element Eigen RowVector, effectively taking the dot product.
Eigen::Array<Eigen::Vector3d, Eigen::Dynamic, Eigen::Dynamic> velField(5, 5);
Eigen::Vector3d n;
// ... initialisation of n and velField not shown
Eigen::Array<double, Eigen::Dynamic, Eigen::Dynamic> result(5, 5);
result = n.transpose() * velField;
This gives a compile error YOU MIXED DIFFERENT NUMERIC TYPES. However, if I don't assign the result, but just compute it:
n.transpose() * velField;
it compiles. What is the correct return type for 'result' - or how else can I fix this?
EDIT
The same problem can be observed in this simpler case (multiplying by a double):
Eigen::Array<Eigen::Vector3d, Eigen::Dynamic, Eigen::Dynamic> velField(5, 5);
// ... initialisation of velField not shown
Eigen::Array<Eigen::Vector3d, Eigen::Dynamic, Eigen::Dynamic> result(5, 5);
result = 3.0 * velField;
However the same code works when using std::complex<double> instead of Eigen::Vector3d:
Eigen::Array<std::complex<double>, Eigen::Dynamic, Eigen::Dynamic> velField(5, 5);
// ... initialisation of velField not shown
Eigen::Array<std::complex<double>, Eigen::Dynamic, Eigen::Dynamic> result(5, 5);
result = 3.0 * velField;
I wonder why it works for std::complex but not with Eigen::Vector3d. Both types define the operator * with a double.

After further reading, I found an answer:
According to the reference documentation of the matrix class, the first template parameter _Scalar is:
_Scalar: matrix_tparam_scalar Numeric type, e.g. float, double, int or std::complex<float>.
User defined sclar types are supported as well.
It links to this page. It lists the requirements for the "custom scalar types". In the above example Eigen::NumTraits for <Vector3d> is missing. It can't be implemented properly for Vector3d, so one should only store types that represent scalars inside an Eigen::Matrix/Eigen::Array.
Update:
The line without assignment n.transpose() * velField; works because of lazy evalutation. It also works when doing this (in C++11):
auto result = n.transpose() * velField;
But it has done no calculation at that point (check the type of result in the debugger). As soon as you use result it will fail the same way than in your first example.

This is not allowed. I'd recommend to store velField as a Matrix, i.e., a 1D array of 3D vectors, and then use standard linear algebra operators.

Related

Converting a torch::Tensor to an Eigen::TensorFixedSize

I have a torch::Tensor whose type T and dimensions Sizes are known at compile time. I would like to convert it to an Eigen::TensorFixedSize<T, Sizes>, by writing a conversion function with an interface that looks like this:
/*
* Reinterprets a torch::Tensor as an Eigen::TensorFixedSize.
*
* This is NOT a copy. Modifying the outputted value will result in
* modifications to the inputted value.
*/
template<typename T, typename Sizes>
Eigen::TensorFixedSize<T, Sizes, Eigen::RowMajor> torch2eigen(torch::Tensor& tensor);
How can I do this?
I found a "Data Transfer Tools" (dtt) package that offers similar conversions here. It provides the following implementation for a conversion from a torch::Tensor to a dynamically sized Eigen::Matrix:
template<typename V>
Eigen::Matrix<V, Eigen::Dynamic, Eigen::Dynamic> libtorch2eigen(torch::Tensor &Tin) {
/*
LibTorch is Row-major order and Eigen is Column-major order.
MatrixXrm uses Eigen::RowMajor for compatibility.
*/
auto T = Tin.to(torch::kCPU);
Eigen::Map<MatrixXrm<V>> E(T.data_ptr<V>(), T.size(0), T.size(1));
return E;
}
I hoped to follow this same recipe for my desired use-case by replacing the template class parameter of Eigen::Map. However, it appears that Eigen::Map only works in the vector/matrix case, and not for tensors of 3 or more dimensions.

How to convert std::vector<std::pair<double, double>> dataVector to Eigen::MatrixXd rtData(dataVector.size(), 2)

Is there a fast way to make conversion below?
use "std::vector<std::pair<double, double>> dataVector" to initialize "Eigen::MatrixXd rtData(dataVector.size(), 2)"
Assuming sizeof(std::pair<int,int>) == 2*sizeof(int) (i.e., std::pair is not adding any padding) you can use an Eigen::Map of a row-major integer matrix, then cast it to double. Something like this:
typedef Eigen::Matrix<int, Eigen::Dynamic, 2, Eigen::RowMajor> MatrixX2i_r;
Eigen::MatrixXd rtData
= Eigen::Map<MatrixX2i_r const>(&(dataVector[0].first), dataVector.size(), 2).cast<double>();
You can also use that expression directly without storing it into a MatrixXd (conversion will happen on the fly, every time the expression is used -- so if you use rtData multiple times, it could still be better to store it once).
I am assuming you wish to create a two column matrix from bunch of pairs stacked in a vector.
As far I can see in the documentation MatrixXd is typedef of Matrix<double,dynamic,dynamic> so first potential issue is that you wish to fill matrix of double by int values.
Since MatrixXd is merely a typedef I don't think there is a suitable constructor in Eigen library for you to do quick conversion.
Simply write a code or a function that iterates through your vector and populates the matrix. In C++14:
int itt=0;
for (const auto& [first, second] : dataVector)
{
rtData(itt,0) = first;
rtData(itt,1) = second;
itt++;
}
That is a neat for loop that can go through vector of pairs, you still need the index of the elements, as Eigen matrices do not have push_back methods as STL.
Note again that I would advise MatrixXi if you are populating it with integers and not doubles.

std::vector<Eigen::Vector3d> to Eigen::MatrixXd Eigen

I would like to know whether is there an easier way to solve my problem rather than use a for loop. So here is the situation:
In general, I would like to gather data points from my sensor (the message is of type Eigen::Vector3d and I can't change this, because it's a huge framework)
Gathered points should be saved in Eigen MatrixXd (in order to process them further as the Matrix in the optimization algorithm), the dimensions apriori of the Matrix are partially unknown, because it depends of me how many measurements I will take (one dimension is 3 because there are x,y,z coordinates)
For the time being, I created a std::vector<Eigen::Vector3d> where I collect points by push_back and after I finished collecting points I would like to convert it to MatrixXd by using the operation Map .
sensor_input = Eigen::Map<Eigen::MatrixXd>(sensor_input_vector.data(),3,sensor_input_vector.size());
But I have an error and note : no known conversion for argument 1 from Eigen::Matrix<double, 3, 1>* to Eigen::Map<Eigen::Matrix<double, -1, -1>, 0, Eigen::Stride<0, 0> >::PointerArgType {aka double*}
Can you tell me how I could implement this by using a map function?
Short answer: You need to write (after making sure that your input is not empty):
sensor_input = Eigen::Map<Eigen::MatrixXd>(sensor_input_vector[0].data(),3,sensor_input_vector.size());
The reason is that Eigen::Map expects a pointer to the underlying Scalar type (in this case double*), whereas std::vector::data() returns a pointer to the first element inside the vector (i.e., an Eigen::Vector3d*).
Now sensor_input_vector[0].data() will give you a pointer to the first (Vector3d) entry of the first element of your std::vector. Alternatively, you could reinterpret_cast like so:
sensor_input = Eigen::Map<Eigen::MatrixXd>(reinterpret_cast<double*>(sensor_input_vector.data()),3,sensor_input_vector.size());
In many cases you can actually avoid copying the data to a Eigen::MatrixXd but instead directly work with the Eigen::Map, and instead of MatrixXd you can use Matrix3Xd to express that it is known at compile time that there are exactly 3 rows:
// creating an Eigen::Map has O(1) cost
Eigen::Map<Eigen::Matrix3Xd> sensor_input_mapped(sensor_input_vector[0].data(),3,sensor_input_vector.size());
// use sensor_input_mapped, the same way you used sensor_input before
You need to make sure that the underlying std::vector will not get re-allocated while sensor_input_mapped is used. Also, changing individual elements of the std::vector will change them in the Map and vice versa.
This solution should work:
Eigen::MatrixXd sensor_input = Eigen::MatrixXd::Map(sensor_input_vector[0].data(),
3, sensor_input_vector.size());
Since your output will be a matrix of 3 x N (N is the number of 3D vectors), you could use the Map function of Eigen::Matrix3Xd too:
Eigen::Matrix3Xd sensor_input = Eigen::Matrix3Xd::Map(sensor_input_vector[0].data(),
3, sensor_input_vector.size());

Eigen: assign value to map

I'm using opencv with Eigen.
Here is a sample on how to link an opencv Mat to an Eigen Matrix:
OpenCV CV::Mat and Eigen::Matrix
The key is to use Eigen's Map type, which can point to the memory allocated by opencv.
According to the Eigen docs, the Map is supposed to be transparent, virtually all operations that work on matrices should work on Map, too.
https://eigen.tuxfamily.org/dox/group__TutorialMapClass.html
Here is a some code from the link above. m2map points to the memory of a matrix called m2:
m2map(3) = 7; // this will change m2, since they share the same array
But for me, even the simplest assignment fails:
Eigen::Map<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> eigenHeatmap(heatmap.ptr<float>(), heatmap.rows, heatmap.cols);
eigenHeatmap=0.f;
produces:
/.../Code.cpp:69:25: error: no match for ‘operator=’ (operand types are ‘Eigen::Map<Eigen::Matrix<float, -1, -1, 1> >’ and ‘float’)
eigenHeatmap=0.f;
The whole error message is rather long: https://pastebin.com/i3AWs6C7
I'm using eigen3.3.3, opencv3.2.0 and g++ 5.4.0
You should try eigenHeatmap.setZero(); or eigenHeatmap.setConstant(0.f); instead.
Alternative to Avi's answer, if you are doing lots of element-wise operations, use Array instead of Matrix, i.e.
Eigen::Map<Eigen::Array<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> eigenHeatmap(heatmap.ptr<float>(), heatmap.rows, heatmap.cols);
eigenHeatmap=0.f;
And if you use that a lot, make some typedefs first:
typedef Eigen::Array<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> ArrayXXfR;
typedef Eigen::Map<ArrayXXfR> ArrayMap;
ArrayMap eigenHeatmap(heatmap.ptr<float>(), heatmap.rows, heatmap.cols);
Addendum: For more details about the Array class, read the corresponding tutorial here.

Creating an Eigen matrix from an array with row-major order

I have an array of doubles, and I want to create a 4-by-4 matrix using the Eigen library. I also want to specify that the data is stored in row-major order. How can I do this?
I have tried the following, but it does not compile:
double data[16];
Eigen::Matrix4d M = Eigen::Map<Eigen::Matrix4d>(data, 4, 4, Eigen::RowMajor);
You need to pass a row-major matrix type to Map, e.g.:
Map<Matrix<double,4,4,RowMajor> > M(data);
then you can use M as an Eigen matrix, and the values of data will be modified, e.g.:
M = M.inverse();
If you want to copy the data to a true column-major Eigen matrix, then do:
Matrix4d M = Map<Matrix<double,4,4,RowMajor> >(data);
Of course, you can also copy to a row-major matrix by using the right type for M.
RowMajor forms actually come in handy when using arrays to store data sometimes. Hence you can also prefer using a typedef to RowMajor type.
namespace Eigen{
typedef Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> MatrixXfRowMajor;
}
You can replace by float by any datatype of choice. For a 4x4 matrix then, we can simply do
Eigen::MatrixXfRowMajor mat;
mat.resize(4,4);

Categories