I am trying to do a reduction based on eigen matrix.
#include <iostream>
#include <Eigen/Dense>
#include <type_traits>
template<typename T1, typename T2, int n1, int n2>
auto reduction(Eigen::Matrix<T1, n1, n2> &a1,
Eigen::Matrix<T2, n1, n2> &a2)
-> decltype(T1{}*T2{})
{
using BaseT3 =
typename std::remove_cv<typename std::remove_reference<decltype(T1{}*T2{})>::type>::type;
BaseT3 res = a1(0, 0)*a2(0, 0);
for (int i=0; i<n1; ++i)
for (int j=0; j<n2; ++j)
if (i+j)
res = res + a1(i, j)*a2(i, j);
return res;
}
int main()
{
Eigen::Matrix<double, 3, 3> m;
Eigen::Matrix<Eigen::Vector3d, 3, 3> n;
std::cout << reduction(m, n) << std::endl;
}
Basically, Im a trying to get sum_{i, j} a1[i, j] * a2[i, j] where a1 and a2 are some eigen mathix but I get compilation errors. The error I get is
error: no match for ‘operator=’ (operand types are ‘BaseT3 {aka
Eigen::CwiseUnaryOp<Eigen::internal::scalar_multiple_op<double>,
const Eigen::Matrix<double, 3, 1> >}’
and
‘const Eigen::CwiseBinaryOp<Eigen::internal::scalar_sum_op<double>,
const Eigen::CwiseUnaryOp<Eigen::internal::scalar_multiple_op<double>,
const Eigen::Matrix<double, 3, 1> >,
const Eigen::CwiseUnaryOp<Eigen::internal::scalar_multiple_op<double>,
const Eigen::Matrix<double, 3, 1> > >’)
res = res + a1(i, j)*a2(i, j);
^
If I am not mistaken, for the given main, type BaseT3 should have been Eigen::Vector3d. I also tried to static cast so the operator= should not fail but I then get other errors.
This is c++11, I use Eigen3 and the compiler is g++ 5.4.1.
The decltype of T1 * T2 isn't what you expect here - Eigen heavily uses expression templates. The CWiseUnaryOp and CWiseBinaryOp types in your error are indicative of that. In other words, the result of "double * Vector3d" isn't what you'd expect (it's not a Vector3d, it's a cwisebinaryop).
See also: Writing functions taking Eigen Types.
In this specific case you may find a solution by creating partial specializations for Eigen base types for both the first and second parameters of your template function.
Eigen uses expression templates to optimize chains of operations.
So MatrixA*MatrixB is not a Matrix type, but rather an expression that says "when evaluated this will be the product of a MatrixA times a MatrixB".
The result is that A*B+C*D doesn't create (at least as many) temporary matrixes, but instead when stored in an output matrix the results are "lazily" calculated directly into the output matrix.
Now, you are multipying elements. But one of your element types is in turn a matrix. And Eigen does expression template optimization of scalar times vector it turns out.
The type you want is std::decay_t<decltype((T1{}+T2{}).eval())> (well the C++11 verbose version of that).
You could write a fancy SFINAE thing that checks if it can be evaluated, and if so does that. Or you can test for Eigen expression template types.
Related
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.
I was trying to define a generic operator for containers as follows:
#include <algorithm>
#include <functional>
namespace test {
template<template <typename...> class _Container,
typename _Type, typename... _Args>
_Container<_Type,_Args...>
operator+(const _Container<_Type,_Args...>& c1,
const _Container<_Type,_Args...>& c2)
{
typedef _Container<_Type,_Args...> container_type;
assert(c1.size() == c2.size());
container_type result;
std::transform(c1.begin(), c1.end(), c2.begin(),
std::back_inserter(result), std::plus<_Type>());
return result;
}
} // test namespace
However, GCC 4.9.2 does not try as a candidate for the following test code:
typedef std::vector<int> vector;
vector v1, v2;
vector result = v1 + v2;
I also tried the above without the parameter pack for the template. Same result.
However, without the namespace declaration, it works fine.
What am I doing wrong? Similar operators defined by the STL in the std namespace are tested as candidates.
The error message is simply:
/tmp/file.cc: In function ‘int main()’:
/tmp/file.cc:28:22: error: no match for ‘operator+’ (operand types are ‘vector {aka std::vector<int>}’ and ‘vector {aka std::vector<int>}’)
I was trying to define a generic operator for containers
uh oh...
Ignoring the ADL issues you will have, there are semantic issues too.
For example, consider:
vector<int> a { 1, 2, 3 };
vector<int> b { 4, 5, 6 };
auto c = a + b; // this won't compile, it's for illustration.
Question: What should the operation do?
Some people might think it should model this:
auto c = concatenate(a, b);
// c == { 1, 2, 3, 4, 5, 6 }
Others might think it should do as you suggest:
auto c = add_elements(a, b);
// c == { 5, 7, 9 }
Who's right?
The answer is that it depends on the context of use of the vector. The vector is a primitive type. It carries no information about the use case. There is simply not enough information available to make an informed choice.
Wrapping the vector into a custom type allows you to provide contextual information and describe the actions of operators correctly.
Of course, you will need to define the arithmetic operators explicitly for the type.
In summary:
The standard library does not define arithmetic operators for containers for good reason. For that same reason, neither should you.
As a parting note, even the transform interpretation is not trivial. What should happen if the vectors are of different size?
I'm trying to write a function using Eigen that inverts a diagonal matrix a little bit different from usual. When the element of the diagonal is zero (or relatively close to zero), it should set the value for the diagonal element to zero, but otherwise the value should be 1/(corresponding element). I tried to write a function that receives the diagonal matrix that I want to invert (it is actually a nx1 matrix, hence the name) and another pointer, where I want the result to be put in:
template <typename m1, typename m2>
void invertSingularValues(Eigen::EigenBase<m1>& sing_val_vector,Eigen::EigenBase<m2>& res)
{
for (int i=0; i<sing_val_vector.rows();i++)
res(i,i)=(sing_val_vector[i]<0.0000001?0:1/sing_val_vector[i]);
};
It seems that I cannot access the elements of the matrices by using (i,j) or [i] as I get this errors:
no match for ‘operator[]’ (operand types are ‘Eigen::EigenBase >’ and ‘int’)
res(i,i)=(sing_val_vector[i]<0.0000001?0:1/sing_val_vector[i]);
no match for ‘operator[]’ (operand types are ‘Eigen::EigenBase >’ and ‘int’)
res(i,i)=(sing_val_vector[i]<0.0000001?0:1/sing_val_vector[i]);
no match for call to ‘(Eigen::EigenBase >) (int&, int&)’
res(i,i)=(sing_val_vector[i]<0.0000001?0:1/sing_val_vector[i]);
When I call the function like this:
invertSingularValues(S.data,S_inv);
S.data and S_inv are Eigen matrices.
What can I do?
As the compiler says, you cannot do res(i,i) when res is of type EigenBase because EigenBase does not have this functionality defined. You need to use a more specific class, like MatrixBase or DenseBase.
Relevant documentation:
Writing functions taking Eigen types as parameters
The class hierarchy
Ignoring any code optimisations, something like this would work:
template <typename m1, typename m2>
void invertSingularValues(Eigen::MatrixBase<m1>& sing_val_vector,Eigen::MatrixBase<m2>& res)
{
for (int i(0); i < sing_val_vector.rows(); i++)
for (int j(0); j < sing_val_vector.cols(); j++)
res(i,j) = sing_val_vector(i,j) < 1e-07 ? 0.0 : 1.0/sing_val_vector(i,j);
};
As an example, suppose I want to write a function that checks whether each floating point number in a dense eigen object is normal, and returns 1.0 for the corresponding position if it is normal, or 0.0 if it is not. The output must not be evaluated immediately, and would preferably be in the form of an expression.
I've read through the documentation on passing eigen objects as parameters, which has led me to attempt the following:
#include <functional>
#include <Eigen/Dense>
template <typename Derived>
auto Fun(const Eigen::DenseBase<Derived>& matrix) -> decltype(matrix.unaryExpr(std::ptr_fun<typename Derived::Scalar, bool>(std::isnormal)).template cast<typename Derived::Scalar>())
{
return matrix.unaryExpr(std::ptr_fun<typename Derived::Scalar, bool>(std::isnormal)).template cast<typename Derived::Scalar>();
}
int main()
{
Eigen::Matrix<double, -1, -1> mat;
mat.resize(100,100);
mat.fill(100);
auto mat2 = Fun(mat);
return 0;
}
This fails with the error that unaryExpr is not defined for an object of type Eigen::DenseBase<Derived>, and sure enough, if I look at the documentation for DenseBase I see that there is no such function.
So then, short of forcing evaluation every time I want to call this function and casting the eigen object to an evaluated Matrix, how can I achieve this?
I need to access the array that contains the data of a MatrixBase Eigen matrix.
The Eigen library has the data() method which returns a pointer to an array, however it is only accessible from a Matrix type. The MatrixBase doesn't have a similar method, even though the MatrixBase class is supposed to act as a template and the actual type should be just a Matrix. If I try to access MatrixBase.data() I get a compile time error:
template <typename ScalarA, typename Index, typename DerivedB, typename DerivedC>
void uscgemv(float alpha,
const USCMatrix<ScalarA,Index> &a,
const MatrixBase<DerivedB> &b,
const MatrixBase<DerivedC> &c_const)
{
//...some code
float * bMat = b.data();
///more code
}
This code produces the following compile time error.
error: ‘const class Eigen::MatrixBase<Eigen::CwiseNullaryOp<Eigen::internal::scalar_constant_op<float>, Eigen::Matrix<float, -1, 1> > >’ has no member named ‘data’
float * bMat = b.data();
So I have to resort to gimmicks such as...
float * bMat;
int bRows = b.rows();
int bCols = b.cols();
mallocPinnedMemory(&bMat, bRows*bCols*sizeof(float));
Eigen::Map<Matrix<float, Dynamic, Dynamic> > bmat_temp(bMat, bRows, bCols);
bmat_temp = b; //THis is SLOW, we should avoid it.
Then I can access the bMat array...
Those copies back-and-forth are the biggest cost in the gpu matrix multiplication, as I essentially I have to make an extra copy, before even coping to the device...
I can't use Eigen-magma, as this is sparse matrix-in-a-weird-format to a dense matrix (or sometimes vector) multiplication so I can't use any of the automatic gpu functions there. Also I would much rather not declare the matrices as something else, because that would require changing A LOT of lines of code across the whole program (which I didn't write).
EDIT: A static cast solution was proposed:
float * bMat = (static_cast<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> >(b)).data();
However I get segfault the first time I try to access an element of the array bMat.
EDIT 2: I'm looking for a zero copy way to access the underlying arrays. I need to only be able to read b, but I also need to able to write to c. Currently c is unconst-d with the following macro:
#define UNCONST(t,c,uc) Eigen::MatrixBase<t> &uc = const_cast<Eigen::MatrixBase<t>&>(c);
EDIT 3: After cross posting to Eigen Forums it would seem I can't do better than the suggested answer.
MatrixBase is the base class of any dense expression. It does not necessarily correspond to an object with storage. For instance, can be the abstract representation of A+B, or in your case the abstract representation of a vector with constant values. You can make uscgemv accepts only expression having appropriate storage using the Ref<> class, e.g.:
template <typename ScalarA, typename Index>
void uscgemv(float alpha,
const USCMatrix<ScalarA,Index> &a,
Ref<const VectorXf> b,
Ref<VectorXf> c);
If the third argument does not match the storage of a VectorXf then it will be evaluated for you. Then you can safely call b.data(). To keep the scalar type of b generic, you can still declare it as MatrixBase<DerivedB>& and then copy it into a Ref<const Matrix<typename DerivedB::Scalar, DerivedB::RowsAtCompileTime, DerivedB::ColsAtCompileTime> >:
typedef Ref<const Matrix<typename DerivedB::Scalar, DerivedB::RowsAtCompileTime, DerivedB::ColsAtCompileTime> > RefB;
RefB actual_b(b);
actual_b.data();
I guess the issue is this: you are not allowed to get a pointer to data of a MatrixBase<Derived>, since the latter can be any kind of expression in Eigen, like a product of matrices for example. To get a pointer you probably have to first implicitly convert the MatrixBase<Derived> into a Matrix<Scalar, Dynamic, Dynamic>, then use the data() member of the latter.
So you can create a deep copy of the expression, i.e. use something like
Eigen::Matrix<typename Derived::Scalar, Eigen::Dynamic, Eigen::Dynamic tmp = b;
then use
tmp.data()
This code works now
#include <Eigen/Dense>
#include <iostream>
template<typename Derived>
void use_data\
(const Eigen::MatrixBase<Derived>& mat)
{
Eigen::Matrix<typename Derived::Scalar, Eigen::Dynamic, Eigen::Dynamic>tmp = mat();
typename Derived::Scalar* p = tmp.data();
std::cout << std::endl;
for(std::size_t i = 0; i < tmp.size(); i++)
std::cout << *(p+i) << " ";
}
int main()
{
Eigen::MatrixXd A = Eigen::MatrixXd::Random(2, 2);
Eigen::MatrixXd B = Eigen::MatrixXd::Random(2, 2);
// now A*B is an expression, of type MatrixBase<EigenSum....>
use_data(A + B);
}
There are an easy solution to solve your question, combine EigenMap, &a(0, 0) and const_cast you could resue the buffer of the MatrixBase.
Example :
template<typename Derived1,
typename Derived2>
void example(Eigen::MatrixBase<Derived1> const &input,
Eigen::MatrixBase<Derived2> const &output)
{
static_assert(std::is_same<Derived1::Scalar, Derived2::Scalar>::value,
"Data type of matrix input, weight, bias and output should be the same");
using Scalar = typename Derived3::Scalar;
using MatType = Eigen::Matrix<Scalar, Eigen::Dynamic, 1>;
using Mapper = Eigen::Map<const MatType, Eigen::Aligned>;
//in the worst case, you can do const_cast<Scalar *> on
//&bias(0, 0).That is, if you cannot explicitly define the Map
//type as const
Mapper Map(&input(0, 0), input.size());
output.colwise() += Map;
}
}
I try it on windows 8, vc2013 32bits, Eigen version is 3.2.5, no segmentation fault occur(yet), every things looks perfectly fine. I also check the address of the Map, it is same as the original input. You can verify it with another example
#include <Eigen/Dense>
#include <iostream>
template<typename Derived>
void example_2(Eigen::MatrixBase<Derived> &input)
{
using Scalar = decltype(input[0]);
Eigen::Map<Derived> map(&input(0, 0),
input.rows(),
input.cols());
map(0, 0) = 300;
}
int main()
{
Eigen::MatrixXd mat(2, 2);
mat<<0, 1, 2, 3;
example_2(mat);
std::cout<<mat<<"\n\n";
return 0;
}
The first element of mat will be "300"