This forum has been archived. All content is frozen. Please use KDE Discuss instead.

template specialization for Eigen types

Tags: None
(comma "," separated)
hijokpayne
Registered Member
Posts
25
Karma
0
I am struggling to do template specilaization for Eigen types.

Say I have a class
Code: Select all
template <class T>
struct multiplyImpl {
....
};

and then I want to specialize this class for Eigen::MatrixBase<Derived> types
Code: Select all
template <class Derived>
struct multiplyImpl <Eigen::MatrixBase<Derived> > {
...
};

But the above specialization will not work with say multiplyImpl<Eigen::Vector3d> since Eigen::Vector3d is not equal to Eigen::MatrixBase<Derived> but simply derives from it. This unfortunately is not good enough for template pattern matching.

The way I know to solve this kind of problem is to do something as the following:
Code: Select all
template <class T, class Enable = void>
struct multiplyImpl {
....
};

and the specialization as

Code: Select all
template <class T>
struct multiplyImpl <T, boost::enable_if < is_eigen_type<T>::value > > {
....
};


Now my actual issue is being able to implement this is_eigen_type<T> which determines if T is an Eigen type. The way I am trying to solve this problem is to check if T derives from EigenDenseBase class as following:
Code: Select all
template <typename T>
struct is_eigen_type : public boost::is_base_of<Eigen::EigenDenseBase<T>, T> {
};


Though this works for actual Eigen types e.g. is_eigen_type<Eigen::Vector2d> but will fail to compile when used for non-Eigen types like is_eigen_type<int>.
This is because it tries to instantiate Eigen::EigenDenseBase<T> which fails for non-Eigen types.

Something like is_eigen_type<T> or is_eigen_matrix_type<T> in my opinion is very useful. Am I missing some similar existing functionality already in Eigen? If not how do I go about this?
User avatar
ggael
Moderator
Posts
3447
Karma
19
OS
Cannot you make multiplyImpl a function for which matching template base classes does work?
hijokpayne
Registered Member
Posts
25
Karma
0
Well my original intention is something like following:

Code: Select all
template<class T>
void multiply(T& a,  const T&b) {
 a *= b;
}


and a partial specialization for Eigen Types like:
Code: Select all
template<class Derived>
void multiply<Eigen::MatrixBase<Derived> >(Eigen::MatrixBase<Derived>& a,  const Eigen::MatrixBase<Derived>& b) {
 const_cast<Eigen::MatrixBase<Derived>&>(a) = a.cwiseProduct(b);
}


But since I cannot have partial specialization for functions, so I am trying to use the following approach as suggested here (Example 4: Illustrating Moral #2)

Code: Select all
template<class T,  class Enable = void>
struct multiplyImpl;

template<class T>
void multiply( T const& a, const T& b) {
  multiplyImpl<T>::multiply(a, b);
}

// Now I can specialize multiplyImpl as I want

template <class T, class Enable = void>
struct multiplyImpl {
....
};


Let me know if there is a simpler approach.
User avatar
ggael
Moderator
Posts
3447
Karma
19
OS
You cannot have partial specializations but you can have overloads!
User avatar
ggael
Moderator
Posts
3447
Karma
19
OS
BTW, why don't you use Eigen::Array instead of Eigen::Matrix if what you want is coefficient wise products?
hijokpayne
Registered Member
Posts
25
Karma
0
I just gave that for simpler example. I am trying to write some generic functions that works on standard Scalar types like float, double and also on Eigen matrices and expressions. For e.g I am trying to write a function that converts probabilities to log-odds like:
Code: Select all
Derived::PlainObject convert(const Eigen::MatrixBase<Derived>& prob) {
    return prob.cwiseQuotient(Derived::Ones() - prob).array().log();
  }

Now I want to also do the same for float types i.e float log_odds = convert(const float& prob);

I feel, ability to specialize for Eigen types is an important one if the users wants to write generic code that works with both Eigen Types and other types.
User avatar
ggael
Moderator
Posts
3447
Karma
19
OS
That's why I suggested to use Eigen::Array:
Code: Select all
#include <Eigen/Dense>
using namespace Eigen;
template<typename T>
T convert(const T &prob) {
  using std::log;
  return log(prob / ( 1. - prob ));
}

int main()
{
  ArrayXf prob = ArrayXf::Random(3);
  ArrayXf res = convert(prob);
  double r = convert(0.5);
}
hijokpayne
Registered Member
Posts
25
Karma
0
Thank a lot. That makes lot of the stuff I am writing right now more simpler. But a solution for template specialization will be great for future use.
infinitei
Registered Member
Posts
4
Karma
0
Here is a Boost solution I am using:

Code: Select all
#include <boost/utility/enable_if.hpp>
#include <boost/mpl/has_xxx.hpp>
#include <boost/mpl/and.hpp>

//! The macro is used for enable_if if T is an Eigen Type
#define ENABLE_IF_EIGEN_TYPE(T)\
    typename boost::enable_if< is_eigen_type<T> >::type

/** The macro enables Eigen new() when required. T can be any type
 *
 * Example Usage:
 *  EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF_REQUIRED(Eigen::Vector2d) will enable Eigen's new()
 *  EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF_REQUIRED(Eigen::Vector3d) will NOT
 *  EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF_REQUIRED(int) will NOT
 */
#define EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF_REQUIRED(T)\
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF(requires_eigen_new_allign<T>::value)

namespace detail {
BOOST_MPL_HAS_XXX_TRAIT_DEF(Scalar)
BOOST_MPL_HAS_XXX_TRAIT_DEF(Index)
BOOST_MPL_HAS_XXX_TRAIT_DEF(StorageKind)
}

/**
 * Traits for checking if T is indeed an Eigen Type
 * @tparam T any Type
 *
 * Example Usage:
 * is_eigen_type<int>::value // evaluates to false
 * is_eigen_type<int>::type // evaluates to false_type
 * is_eigen_type<Eigen::Vector2d>::value // evaluates to true
 * is_eigen_type<Eigen::Vector2d>::type // true_type
 */
template<typename T>
struct is_eigen_type:
    boost::mpl::and_<
      detail::has_Scalar<T>,
      detail::has_Index<T>,
      detail::has_StorageKind<T> > {
};


template<class T, class Enable = void>
struct requires_eigen_new_allign {
  static const bool value = false;
};

template<class T>
struct requires_eigen_new_allign<T, ENABLE_IF_EIGEN_TYPE(T)> {
  typedef typename T::Scalar Scalar;
  static const bool value = (((T::SizeAtCompileTime) != Eigen::Dynamic)
      && ((sizeof(Scalar) * (T::SizeAtCompileTime)) % 16 == 0));
};


Basically you can now use ENABLE_IF_EIGEN_TYPE(T) macro to specialize for Eigen Types like

Code: Select all
template <class T>
struct multiplyImpl <T, ENABLE_IF_EIGEN_TYPE(T) > {
....
};


With C++11, you can just replace boost::enable_if with std::enable_if. And C++11 version of is_base_of<Eigen::EigenBase<T>, T> should work with arbitrary T which makes the Boost MPL stuff unnecessary.

@ggael and Eigen authors: A non-boost c++03 solution built into Eigen will be nice. What do you guys think?


Bookmarks



Who is online

Registered users: Baidu [Spider], Bing [Bot], Google [Bot], rblackwell