Rcpp Eigen Map Error with MatrixXf - c++

Why does the following code not compile?
library(Rcpp)
cppFunction('
int rows(const NumericMatrix& X) {
using Eigen::MatrixXf;
typedef Eigen::Map<MatrixXf> MapMat;
MapMat X1(as<MapMat>(X));
return X1.rows();
}', depends = "RcppEigen")
It throws the following error:
error: no matching function for call to 'Eigen::Map<Eigen::Matrix<float, -1, -1> >::Map(Rcpp::Vector<14, Rcpp::PreserveStorage>::iterator, int&, int&)'
OUT get() {return OUT(vec.begin(), d_nrow, d_ncol );}
The same code works fine when I used MatrixXd instead.
Thanks.

NumericMatrix uses type double (as opposed to float). Eigen does not support implicit type casting between matrices using different types. Your code appears to try and read the memory of a double NumericMatrix as a float Eigen matrix. Just use the MatrixXd type instead.

Related

Passing the Lower Cholesky Factor of an Eigen matrix as a function parameter

I would like to preface this by saying I am a C++ novice, so please be verbose in your comments and/or suggestions.
I am trying to refactor some code. One of the operations I perform involves taking a (memoized) Eigen::LLT<Eigen::MatrixXd> type object from a list and performing some calculations with it.
I would like to refactor this calculation into a smaller function, but I am having trouble passing the Eigen::LLT<Eigen::MatrixXd> type as a parameter while adhering to (my admittedly wobbly understanding of) the advice in the Eigen documentation here.
I have tried the following:
#define _USE_MATH_DEFINES
#include <Eigen/LU>
#include <Eigen/Dense>
#include <math.h>
#include <unsupported/Eigen/SpecialFunctions>
Eigen::MatrixXd conditionalCov(
Eigen::Ref<const Eigen::MatrixXd> kxstarxstar,
Eigen::Ref<const Eigen::LLT<Eigen::MatrixXd>> lxx,
Eigen::Ref<const Eigen::MatrixXd> kxxstar
)
{
return (
kxstarxstar.array() - (kxxstar.transpose() * lxx.solve(kxxstar)).array()
).matrix().selfadjointView<Eigen::Lower>();
}
But this does not compile because of the type definition for lxx, namely Eigen::Ref<const Eigen::LLT<Eigen::MatrixXd>>, is wrong. GCC says:
[<path_to_file>] error: ‘IsVectorAtCompileTime’ is not a member of ‘const Eigen::LLT<Eigen::Matrix<double, -1, -1>, 1>’
5 | Eigen::Ref<const Eigen::LLT<Eigen::MatrixXd>> lxx,
What should the type of lxx (the lower Cholesky factor) be here to avoid creation of temporary matrices when calling the function?
Just pass the LLT object as a standard C++ constant reference.
Passing kxstarxstar and kxxstar by Eigen::Ref also only makes sense, if you intend to pass sub-blocks of other matrices. If not, just pass them as const Eigen::MatrixXd &. If you want to pass them as Eigen::Ref, it is recommended to pass that itself as const &:
Eigen::MatrixXd conditionalCov(
const Eigen::Ref<const Eigen::MatrixXd>& kxstarxstar,
const Eigen::LLT<Eigen::MatrixXd> &lxx,
const Eigen::Ref<const Eigen::MatrixXd>& kxxstar
)
N.B.: Inside your function there is no need to convert between Matrix and Array types, just the following should give the same result:
return (
kxstarxstar - kxxstar.transpose() * lxx.solve(kxxstar)
).selfadjointView<Eigen::Lower>();

Eigen binaryExpr with eigen type output

I'm having a problem while trying to use binaryExpr. It is the first use I'm making of it so I have been following the Eigen documentation
For my use I need a functor with Eigen type inputs and outputs but this does not want to compile and I do not understand why. I've looked up the explanation in the code but I didn't think this would apply here because I use floats and an array of floats
// We require Lhs and Rhs to have "compatible" scalar types.
// It is tempting to always allow mixing different types but remember that this is often impossible in the vectorized paths.
// So allowing mixing different types gives very unexpected errors when enabling vectorization, when the user tries to
// add together a float matrix and a double matrix.
Here is a short example of the use I would need that gets me the same compilation error:
#include <eigen3/Eigen/Dense>
using namespace std;
using namespace Eigen;
struct myBinaryFunctor {
EIGEN_EMPTY_STRUCT_CTOR(myBinaryFunctor)
typedef Vector2f result_type;
Vector2f operator()(const Matrix<float,9,1>& a,const float& f) const
{
float x = a.head(4).sum()*f;
float y = a.tail(5).sum()/f;
return Vector2f(x,y);
}
};
int main()
{
constexpr int n = 3;
Matrix<Matrix<float,9,1>,n,n> Ma;
Matrix<float,n,n> F;
Matrix<Vector2f,n,n> R;
for(size_t i = 0, sizeMa = Ma.size(); i<sizeMa; i++)
{
Ma(i).setOnes();
}
F.setConstant(n,n,2);
R = Ma.binaryExpr(F,myBinaryFunctor());
return 0;
}
The compilation output is :
/usr/local/include/eigen3/Eigen/src/Core/CwiseBinaryOp.h:107: erreur : static assertion failed: YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY
EIGEN_CHECK_BINARY_COMPATIBILIY(BinaryOp,typename Lhs::Scalar,typename Rhs::Scalar);
^
If you have a solution that could make this work this would be a huge help for me :) If not I would still enjoy an explanation to understand what is happening. Thanks a lot.
Adding:
namespace Eigen {
template<>
struct ScalarBinaryOpTraits<Matrix<float,9,1>,float,myBinaryFunctor> {
typedef Vector2f ReturnType;
};
}
will do the job. This is because implicit scalar conversion are explicitly disallowed within Eigen, so you must explicit say that two different scalar types are compatible. For instance adding a VectorXd to a VectorXf is disallowed.
Nonetheless, it seems to me that your abusing Eigen's features here.

Passing fixed-size Eigen matrices as arguments to function calling for dynamic-size matrices

I am writing a small linear algebra utility library on top of Eigen for my personal code-base. To try to make it as flexible as possible, I typedefed different Eigen matrix types for use as parameters. However, an issue I keep running into is that when I use it, I can't pass a fixed-size (i.e. set at compile-time) matrix as the argument to a function that has a dynamically-sized (set at runtime) matrix typedef as a parameter. I could understand the inverse--not being able to pass a dynamic-sized matrix as fixed due to compile-time checks, but it seems this ought to work.
A test-able example is the pdist2 function below (which really ought to have a native implementation in the Eigen API).
#include <Eigen/Core>
namespace Eigen
{
template <typename T>
using MatrixXT = Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>;
}
// X is M x N
// Y is M x K
// Output is N x K
template <typename T>
inline Eigen::MatrixXT<T> pdist2(const Eigen::MatrixXT<T> &X, const Eigen::MatrixXT<T> &Y)
{
// ASSERT(X.rows() == Y.rows(), "Input observations must have same number of rows (" +
// std::to_string(X.rows()) + "!=" + std::to_string(Y.rows()) + ")");
Eigen::MatrixXT<T> dists = X.colwise().squaredNorm().transpose() * Eigen::MatrixXT<T>::Ones(1, Y.cols()) +
Eigen::MatrixXT<T>::Ones(X.cols(), 1) * Y.colwise().squaredNorm() -
2 * X.transpose() * Y;
return dists;
}
This code does not compile:
Eigen::Matrix<double, 3, 5> X;
X << 8.147236863931790, 9.133758561390193, 2.784982188670484, 9.648885351992766, 9.571669482429456,
9.057919370756192, 6.323592462254095, 5.468815192049838, 1.576130816775483, 4.853756487228412,
1.269868162935061, 0.975404049994095, 9.575068354342976, 9.705927817606156, 8.002804688888002;
Eigen::Matrix<double, 3, 4> Y;
Y << 1.418863386272153, 7.922073295595544, 0.357116785741896, 6.787351548577734,
4.217612826262750, 9.594924263929030, 8.491293058687772, 7.577401305783335,
9.157355251890671, 6.557406991565868, 9.339932477575505, 7.431324681249162;
Eigen::Matrix<double, 5, 4> D = pdist2(X, Y);
The above function has been unit tested and evaluates correctly, but it will only work if X and Y are Eigen::MatrixXd types. It seems it must be my template typedef causing the issue, but it's just a dynamically (i.e. at runtime) sized matrix with a templated type.
The error reads:
error: no matching function for call to ‘pdist2(Eigen::Matrix<double, 3, 5>&, Eigen::Matrix<double, 3, 4>&)’
Eigen::Matrix<double, 5, 4> D = Util::Math::pdist2(X, Y);
^
note: candidate: template<class T> Eigen::MatrixXT<T> Util::Math::pdist2(Eigen::MatrixXT<T>&, Eigen::MatrixXT<T>&)
inline Eigen::MatrixXT<T> pdist2(const Eigen::MatrixXT<T> &X, const Eigen::MatrixXT<T> &Y)
^
note: template argument deduction/substitution failed:
note: template argument ‘3’ does not match ‘#‘integer_cst’ not supported by dump_decl#<declaration error>’
Eigen::Matrix<double, 5, 4> D_est = Util::Math::pdist2(X, Y);
^
note: ‘Eigen::Matrix<double, 3, 5>’ is not derived from ‘Eigen::MatrixXT<T>’
Why is this not working? Or perhaps more specifically, how do I use a templated typedef to ensure that my fixed-size matrices do derive from Eigen::MatrixXT<T>?
Note: This is all using Eigen 3.3.3.
The problem is that Matrix<double, 3, 5> and MatrixXT<double> are not the same type, therefore, the only way to pass the former to pdist2 is to convert it to a MatrixXT<double>. This would be done automatically by the compiler if pdist2 were not a template function:
MatrixXT<double> pdist2(const MatrixXT<double>&,const MatrixXT<double>&);
but since pdist2 is templated and that MatrixXT<double> is not a base class of Matrix<double, 3, 5>, in C++ the compiler is not allowed to automatically deduced the template parameter to instantiate pdist2. The solution for you is to generalize even more your function to take any MatrixBase<> as inputs:
template<typename D1,typename D2>
Matrix<typename D1::Scalar,D1::ColsAtCompileTime,D2::ColsAtCompileTime>
pdist2(const MatrixBase<D1>& _X,const MatrixBase<D2>& _Y);
Since X and Y are going to be used multiple times, and that they can now be arbitrary expressions (including a costly matrix product), you might want to let Eigen evaluate the arguments if needed, to this end, you can use Ref:
Ref<const typename D1::PlainObject> X(_X);
Ref<const typename D2::PlainObject> Y(_Y);
This way X will be evaluated if it cannot be represented as a pointer+stride to actual values.

How to pass Blitz++ subarray as an input/output parameter of a procedure

I have an FFT procedure fftconvx taking two tensors Ttnsr and S as input parameters and producing a result into another tensor G. All tensors are defined as Blitz++ arrays Array<complex<double>, N>, where N is the rank of the array. The procedure fftconvx has to be called multiple times inside of a double loop.
Ideally I would like to pass subarrays Stnsr(ri,rj,rk,0) or Stnsr(ri,rj,rk,1) and receive the result into subarrays Gtnsr(t,p,ri,rj,rk,0) or Gtnsr(t,p,ri,rj,rk,1) as follows:
fftconvx( Gtnsr(t,p,ri,rj,rk,0), Ttnsr, Stnsr(ri,rj,rk,0) );
Variables ri,rj,rk are Blitz++ array ranges. Unfortunately this does not work and results in the following compilation error:
error: invalid initialization of non-const reference of type
‘blitz::Array<std::complex<double>, 3>&’ from an rvalue of type
‘blitz::SliceInfo<std::complex<double>, int, int, blitz::Range, blitz::Range,
blitz::Range, int, blitz::nilArraySection, blitz::nilArraySection,
blitz::nilArraySection, blitz::nilArraySection, blitz::nilArraySection>::T_slice
{aka blitz::Array<std::complex<double>, 3>}’
fftconvx(Gtnsr(t,p,ri,rj,rk,0), Ttnsr, Stnsr(ri,rj,rk,0));
Signature of the fftconvx is:
void fftconvx(Array<complex<double>, 3> &c,
Array<complex<double>, 3> x2,
Array<complex<double>, 3> x1,
...);
There more arrays and variables passed as input parameters, but I omit them for brevity.
So far I have come up with the solution based on temporary arrays S and G:
S(ri,rj,rk) = Stnsr(ri,rj,rk,0);
fftconvx(G, Ttnsr, S);
Gtnsr(t,p,ri,rj,rk,0) = G(ri,rj,rk);
I believe there is a more elegant solution.
Without knowing Blitz++ I offer this possible solution.
It looks like Gtnsr is a SliceInfo and not a Array, but that is has a operator Array.
So changing the fftconvx into
template<class SliceOrArray>
void fftconvx(SliceOrArray &c,
const Array<complex<double>, 3> x2,
const Array<complex<double>, 3> x1,
...);
might work if the operations in fftconvx allows the use of the slice.
If Blitz++ is opdated to C++11 the following might work as well.
G fftconvx( const Array<complex<double>, 3> x2,
const Array<complex<double>, 3> x1,
...) {
G c;
...
return c; // C++11 NRVO
};
and then calling
Gtnsr(t,p,ri,rj,rk,0) = fftconvx( ... );

cannot convert from 'const Eigen::GeneralProduct<Lhs,Rhs,ProductType>' to 'double'

I keep getting this error every time I compute this line
double k = b.transpose()*Z.inverse()*b;
where Eigen::MatrixXd Z(3,3), b(3,1);. I've tried casting but no luck. Any suggestions?
The result is by default an Eigen expression (think of it as a matrix, technically is a template type called Eigen::GeneralProduct<...>), so even if the matrix is 1 x 1, it is not implicitly convertible to double. What you have to do is to access its (0) element (or (0,0), it makes no difference), see below
Eigen::MatrixXd Z(3,3), b(3,1);
double k = (b.transpose()*Z.inverse()*b)(0);
PS: As mentioned by #ggael, you should use an Eigen::VectorXd as the type of b, in which case the result is implicitly convertible to a double.
This works for me, so make sure that b is declared as a VectorXd such that Eigen can know at compile-time that result is a 1x1 matrix and so it can be safely converted to a scalar value. Here is a self-contained example:
#include <Eigen/Dense>
using namespace Eigen;
int main() {
int n = 10;
VectorXd b(n);
MatrixXd Z(n,n);
double k = b.transpose() * Z.inverse() * b;
}