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

NullaryExpr Guidance?

Tags: None
(comma "," separated)
User avatar
willo
Registered Member
Posts
14
Karma
0
OS

NullaryExpr Guidance?

Wed Jan 26, 2011 12:59 am
Eigen Folks,

I'm trying to use Eigen3's NullaryExpr type, but have experiencing some compiler errors, and cannot find much in the Doxygen about this functionality.

Here is my source code:
Code: Select all
#include <Eigen/Dense>

struct DummyGenerator
{
  typedef int result_type;
  result_type operator()()
  {
    return 42;
  }
}; // struct DummyGenerator

int main( int argc, char* argv[] )
{
  Eigen::Vector4d vector = Eigen::Vector4d::NullaryExpr( DummyGenerator() );
  return 0;
} // main


I'm using Linux G++ 4.4.5, and Eigen 3.0-beta2.

I get the following compilation errors & warnings:
In file included from .../Eigen/Core:278,
from .../Eigen/Dense:1,
from test-eigen-nullary-expr.cpp:1:
.../Eigen/src/Core/CwiseNullaryOp.h: In instantiation of 'Eigen::ei_traits<Eigen::CwiseNullaryOp<DummyGenerator, Eigen::Matrix<double, 4, 1, 0, 4, 1> > >':
.../Eigen/src/Core/CwiseNullaryOp.h:61: instantiated from 'Eigen::CwiseNullaryOp<DummyGenerator, Eigen::Matrix<double, 4, 1, 0, 4, 1> >'
test-eigen-nullary-expr.cpp:14: instantiated from here
.../Eigen/src/Core/CwiseNullaryOp.h:48: error: 'IsRepeatable' is not a member of 'Eigen::ei_functor_traits<DummyGenerator>'
.../Eigen/src/Core/CwiseNullaryOp.h: In member function 'const typename Eigen::ei_traits<Eigen::CwiseNullaryOp<NullaryOp, PlainObjectType> >::Scalar Eigen::CwiseNullaryOp<NullaryOp, MatrixType>::coeff(typename Eigen::ei_traits<Eigen::CwiseNullaryOp<NullaryOp, PlainObjectType> >::Index, typename Eigen::ei_traits<Eigen::CwiseNullaryOp<NullaryOp, PlainObjectType> >::Index) const [with NullaryOp = DummyGenerator, PlainObjectType = Eigen::Matrix<double, 4, 1, 0, 4, 1>]':
.../Eigen/src/Core/DenseCoeffsBase.h:466: instantiated from 'void Eigen::DenseCoeffsBase<Derived, (Eigen::AccessorLevels)1u>::copyCoeff(typename Eigen::ei_traits<Derived>::Index, typename Eigen::ei_traits<Derived>::Index, const Eigen::DenseBase<OtherDerived>&) [with OtherDerived = Eigen::CwiseNullaryOp<DummyGenerator, Eigen::Matrix<double, 4, 1, 0, 4, 1> >, Derived = Eigen::Matrix<double, 4, 1, 0, 4, 1>]'
.../Eigen/src/Core/DenseCoeffsBase.h:491: instantiated from 'void Eigen::DenseCoeffsBase<Derived, (Eigen::AccessorLevels)1u>::copyCoeffByOuterInner(typename Eigen::ei_traits<Derived>::Index, typename Eigen::ei_traits<Derived>::Index, const Eigen::DenseBase<OtherDerived>&) [with OtherDerived = Eigen::CwiseNullaryOp<DummyGenerator, Eigen::Matrix<double, 4, 1, 0, 4, 1> >, Derived = Eigen::Matrix<double, 4, 1, 0, 4, 1>]'
.../Eigen/src/Core/Assign.h:155: instantiated from 'static void Eigen::ei_assign_DefaultTraversal_CompleteUnrolling<Derived1, Derived2, Index, Stop>::run(Derived1&, const Derived2&) [with Derived1 = Eigen::Matrix<double, 4, 1, 0, 4, 1>, Derived2 = Eigen::CwiseNullaryOp<DummyGenerator, Eigen::Matrix<double, 4, 1, 0, 4, 1> >, int Index = 0, int Stop = 4]'
.../Eigen/src/Core/Assign.h:284: instantiated from 'static void Eigen::ei_assign_impl<Derived1, Derived2, 0, 2>::run(Derived1&, const Derived2&) [with Derived1 = Eigen::Matrix<double, 4, 1, 0, 4, 1>, Derived2 = Eigen::CwiseNullaryOp<DummyGenerator, Eigen::Matrix<double, 4, 1, 0, 4, 1> >]'
.../Eigen/src/Core/Assign.h:508: instantiated from 'Derived& Eigen::DenseBase<Derived>::lazyAssign(const Eigen::DenseBase<OtherDerived>&) [with OtherDerived = Eigen::CwiseNullaryOp<DummyGenerator, Eigen::Matrix<double, 4, 1, 0, 4, 1> >, Derived = Eigen::Matrix<double, 4, 1, 0, 4, 1>]'
.../Eigen/src/Core/DenseStorageBase.h:311: instantiated from 'Derived& Eigen::DenseStorageBase<Derived>::lazyAssign(const Eigen::DenseBase<OtherDerived>&) [with OtherDerived = Eigen::CwiseNullaryOp<DummyGenerator, Eigen::Matrix<double, 4, 1, 0, 4, 1> >, Derived = Eigen::Matrix<double, 4, 1, 0, 4, 1>]'
.../Eigen/src/Core/Assign.h:529: instantiated from 'static Derived& Eigen::ei_assign_selector<Derived, OtherDerived, false, false>::run(Derived&, const OtherDerived&) [with Derived = Eigen::Matrix<double, 4, 1, 0, 4, 1>, OtherDerived = Eigen::CwiseNullaryOp<DummyGenerator, Eigen::Matrix<double, 4, 1, 0, 4, 1> >]'
.../Eigen/src/Core/DenseStorageBase.h:481: instantiated from 'Derived& Eigen::DenseStorageBase<Derived>::_set_noalias(const Eigen::DenseBase<OtherDerived>&) [with OtherDerived = Eigen::CwiseNullaryOp<DummyGenerator, Eigen::Matrix<double, 4, 1, 0, 4, 1> >, Derived = Eigen::Matrix<double, 4, 1, 0, 4, 1>]'
.../Eigen/src/Core/Matrix.h:286: instantiated from 'Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>::Matrix(const Eigen::MatrixBase<OtherDerived>&) [with OtherDerived = Eigen::CwiseNullaryOp<DummyGenerator, Eigen::Matrix<double, 4, 1, 0, 4, 1> >, _Scalar = double, int _Rows = 4, int _Cols = 1, int _Options = 0, int _MaxRows = 4, int _MaxCols = 1]'
test-eigen-nullary-expr.cpp:14: instantiated from here
.../Eigen/src/Core/CwiseNullaryOp.h:81: error: no match for call to '(const DummyGenerator) (Eigen::DenseIndex&, Eigen::DenseIndex&)'
test-eigen-nullary-expr.cpp:6: note: candidates are: int DummyGenerator::operator()()


I expect this code to compile and work similarly to methods like 'Constant', 'Ones' and 'Zero'.

Thanks for considering,

-- Charles Wilcox
ri_aje
Registered Member
Posts
8
Karma
0

Re: NullaryExpr Guidance?

Wed Jan 26, 2011 2:19 am
here is one way to do it


#include <Eigen/Dense>

struct DummyGenerator
{
typedef int result_type;
result_type operator()(Eigen::DenseIndex&) const
{
return 42;
}
}; // struct DummyGenerator

namespace Eigen
{
template<> struct ei_functor_traits <DummyGenerator>
{ enum { Cost = 1, PacketAccess = false, IsRepeatable = true }; };
}

int main( int argc, char* argv[] )
{
Eigen::Vector4d vector = Eigen::Vector4d::NullaryExpr( DummyGenerator() );
return 0;
} // main

Did not spend a lot time dig around, so I am not sure what does that IsRepeatable mean. I think it should be ok to set it to true for your case.
User avatar
willo
Registered Member
Posts
14
Karma
0
OS

Re: NullaryExpr Guidance?

Wed Jan 26, 2011 4:08 am
ri_aje,

Thanks for the quick reply. I tried your mods to my example, and indeed it compiles. Thanks for that.

Do you, or anyone else, have a reference to docs on doing this properly? I'd like to know things like:
- What do all the values of the apparently required enum mean?
- Why isn't there a default template implementation for any functor?
- What's the required API and expected behavior of functor's 'operator()'?

I actually have a more sophisticated implementation at work that has state and has a relatively high "Cost", and would like to clarify how best to implement and describe this to Eigen.

-- Charles Wilcox
User avatar
ggael
Moderator
Posts
3447
Karma
19
OS

Re: NullaryExpr Guidance?

Wed Jan 26, 2011 8:57 pm
the Cost will control unrolling, and allow us to determine if it is better to evaluate it N times or to evaluate it once in a temporary and then read N times the temporary (e.g.: for matrix products).

PacketAccess: tell whether your functor is vectorized, if so you must provide a packet() method

Repeatable : set it to true if it always return the same, and false otherwise (e.g.: a random number generator will have to set Repeatable to false)

There is a default with cost=10, PacketAccess=false and Repeatable=true

If M is a nullary expression with associated functor f, then:

M(i,j) == f()
User avatar
willo
Registered Member
Posts
14
Karma
0
OS

Re: NullaryExpr Guidance?

Wed Jan 26, 2011 10:21 pm
ggael,

Thanks for this explanation.

My functor has a TR1 'random' variate_generator, which clips results to min/max parameters.

So, it sounds like I should be using:
- IsRepeatable = false. // It has state that changes on every call.
- PacketAccess = false. // I haven't vectorized my functor, neither are the TR1 'random' objects.
- Cost = Some_large_unknown_value. // I'm using a mersenne-twister and a gaussian distribution.

Is there any implicit "units" for the Cost value, such as CPU cycles?

Speculation: Since 'IsRepeatable' will be false, the result will always have to be cached in a temporary, so the Cost-value is moot for that purpose? Then it should just affect loop-unrolling heuristics?

Less importantly, if there is a default ei_functor_traits, why would my example not utilize it? I can't immediately find where this is defined in the eigen code.

-- Charles Wilcox
User avatar
ggael
Moderator
Posts
3447
Karma
19
OS

Re: NullaryExpr Guidance?  Topic is solved

Thu Jan 27, 2011 10:33 am
You are right for all the points. The unit is in CPU cycles, and in your case you can simply use a large values like 1000 to totally disable unrolling.
User avatar
willo
Registered Member
Posts
14
Karma
0
OS

Re: NullaryExpr Guidance?

Mon Jan 31, 2011 2:58 am
ggael,

Thanks for confirming my intuitions, and contributing to such a useful library.

-- Charles Wilcox
ibaltiyskiy
Registered Member
Posts
2
Karma
0

Re: NullaryExpr Guidance?

Sun May 05, 2013 6:43 am
Charles, how did you manage to pass a random number generator to NullaryExpr? I'm trying to do the same (albeit with boost variate_generator), and I'm having problems with constness:

Code: Select all
1>d:\dev\eigen\eigen\src\core\cwisenullaryop.h(82): error C3849: function-style call on an expression of type 'const boost::random::variate_generator<Engine,Distribution>' would lose const and/or volatile qualifiers for all 2 available operator overloads
1>          with
1>          [
1>              Engine=boost::random::mt19937 &,
1>              Distribution=boost::random::chi_squared_distribution<>
1>          ]
1>          d:\dev\eigen\eigen\src\core\cwisenullaryop.h(81) : while compiling class template member function 'const double Eigen::CwiseNullaryOp<NullaryOp,PlainObjectType>::coeff(__w64 int) const'
1>          with
1>          [
1>              NullaryOp=boost::random::variate_generator<boost::random::mt19937 &,boost::random::chi_squared_distribution<>>,
1>              PlainObjectType=Eigen::Matrix<double,-1,-1>
1>          ]
1>          d:\dev\eigen\eigen\src\core\densecoeffsbase.h(495) : see reference to function template instantiation 'const double Eigen::CwiseNullaryOp<NullaryOp,PlainObjectType>::coeff(__w64 int) const' being compiled
1>          with
1>          [
1>              NullaryOp=boost::random::variate_generator<boost::random::mt19937 &,boost::random::chi_squared_distribution<>>,
1>              PlainObjectType=Eigen::Matrix<double,-1,-1>
1>          ]
1>          d:\dev\ml\surfingbird-ml\cpp\bpmf\bpmf2.cpp(121) : see reference to class template instantiation 'Eigen::CwiseNullaryOp<NullaryOp,PlainObjectType>' being compiled
1>          with
1>          [
1>              NullaryOp=boost::random::variate_generator<boost::random::mt19937 &,boost::random::chi_squared_distribution<>>,
1>              PlainObjectType=Eigen::Matrix<double,-1,-1>
1>          ]
1>
Swdshchf
Registered Member
Posts
5
Karma
0

Re: NullaryExpr Guidance?

Mon May 06, 2013 4:37 pm
ibaltiyskiy wrote:Charles, how did you manage to pass a random number generator to NullaryExpr? I'm trying to do the same (albeit with boost variate_generator), and I'm having problems with constness:

I ran into this too. It's annoying. The built-in Random() NullaryExpr uses rand(), which changes a global variable. I flagged my rng as static and the generator object as mutable in my normal-distribution NullaryExpr. If the random number generator isn't static, then it's reseeded every time I use it (making it substantially less useful for random numbers).

Code: Select all
#include <Eigen/Dense>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/normal_distribution.hpp>   

/*
  We need a functor that can pretend it's const,
  but to be a good random number generator
  it needs mutable state.  The standard Eigen function
  Random() just calls rand(), which changes a global
  variable.
*/
namespace Eigen {
namespace internal {
template<typename Scalar>
struct scalar_normal_dist_op
{
  static boost::mt19937 rng;                        // The uniform pseudo-random algorithm
  mutable boost::normal_distribution<Scalar> norm;  // The gaussian combinator

  EIGEN_EMPTY_STRUCT_CTOR(scalar_normal_dist_op)

  template<typename Index>
  inline const Scalar operator() (Index, Index = 0) const { return norm(rng); }
};

template<typename Scalar>
boost::mt19937 scalar_normal_dist_op<Scalar>::rng;

template<typename Scalar>
struct functor_traits<scalar_normal_dist_op<Scalar> >
{ enum { Cost = 50 * NumTraits<Scalar>::MulCost, PacketAccess = false, IsRepeatable = false }; };

} // end namespace internal
ibaltiyskiy
Registered Member
Posts
2
Karma
0

Re: NullaryExpr Guidance?

Tue May 07, 2013 8:43 pm
Thanks a lot! Indeed, I tried to do something similar, but seemed to miss something; now I've finally got it.

As an aside, it looks to me that instead of having static rng, you could as well keep a reference to it and store elsewhere; thus you could also use it in other operations as well, improving the quality of generated numbers.


Bookmarks



Who is online

Registered users: Bing [Bot], Google [Bot], Sogou [Bot]