Re: [eigen] The "nested" concept

[ Thread Index | Date Index | More lists.tuxfamily.org/eigen Archives ]



Guys! This was very interesting. Typically the kind of thing I would see fit in the 'developers' documentation'!

Thomas
-- 
Thomas Capricelli <orzel@xxxxxxxxxxxxxxx>
http://www.freehackers.org/thomas

In data martedì 29 giugno 2010 23:27:45, Gael Guennebaud ha scritto:
> Hi,
> 
> let's pick a simple example:
> 
> MatrixXf A, B, C;
> C = A.transpose() + B;
> 
> here A.transpose() returns an object of type:
> 
> Transpose<MatrixXf>
> 
> where the MatrixXf A is nested into the Transpose expression. The
> nested type tells how to store the nested object. Here
> MatrixXf::Nested boils down to a MatrixXf&, and thus "A.transpose()"
> stores a reference to A.
> 
> Now, you might wonder why we introduced such a nesting type mechanism
> and not always use a reference. There are two main reasons:
> 
> 1 - Unlike Matrix or Array, other expressions are lightweight and
> better nested by value. In the previous example, "A.transpose() + B"
> returns an object of type CwiseBinaryOp<ei_scalar_sum_op<float>,
> Transpose<MatrixXf>, MatrixXf>  storing both sides of the addition as
> follow:
> 
> Transpose<MatrixXf>::Nested lhs; // left hand side
> MatrixXf::Nested rhs; // right hand side
> 
> which boils down to:
> 
> const Transpose<MatrixXf> lhs; // nesting by value
> const MatrixXf& rhs; // nesting by reference
> 
> Nesting by value small object avoids temporary headache when a
> function has to return complex expressions, e.g.:
> 
> template<typename A, typename B>
> CwiseBinaryOp<ei_scalar_sum_op<float>, Transpose<A>, B> adjoint(const
> A& a, const B& b)
> {
>   return a.transpose() + b;
> }
> 
> If the temporary "a.transpose()" was stored by reference by the
> CwiseBinaryOp expression, then you would end up with a segfault
> because the "a.transpose()" temporary is destroyed just before the
> function returns, and so the CwiseBinaryOp expression would store a
> reference to dead object...
> 
> 2 - Another reason is to allow to handle expressions which has to be
> evaluated into a temporary before being used. For instance, in the
> following example:
> 
> d = a * b + c;
> 
> for performance reason, the matrix product a * b has to be evaluated
> into a temporary before evaluating the addition. This is achieved as
> follow. Here we build the expression of type:
> 
> CwiseBinaryOp<ei_scalar_sum_op<float>, Product<MatrixXf,MatrixXf>, MatrixXf>
> 
> which stores a Product<MatrixXf,MatrixXf>::Nested for the lhs, and
> Product<MatrixXf,MatrixXf>::Nested is ... MatrixXf !!
> 
> Something more complicated:
> 
> (a + b) * c
> 
> Here, if c is not too small, it is better to evaluate (a+b) into a
> temporary before doing the matrix product, otherwise, a+b would be
> computed c.cols() times... To this end we have a ei_nested<> helper
> class to determine the ideal nesting type. In Product, we have
> something like:
> 
> ei_nested<CwiseBinaryOp<ei_scalar_sum_op<float>, type_of_a,
> type_of_b>, type_of_c::ColsAtCompileTime>::type
> 
> giving us the nesting type of the left hand side of the product (here
> a MatrixXf if a, b, and c are MatrixXf). For the right hand side here
> we have:
> 
> ei_nested<CwiseBinaryOp<ei_scalar_sum_op<float>, type_of_c,
> type_of_a_plus_b::RowsAtCompileTime>::type
> 
> which gives us a MatrixXf&.
> 
> ei_nested<> determines whether the nested expression has to be
> evaluated or not in function of an estimation of the evaluation cost
> of one coefficient. This cost is automatically computed by the
> expressions in the ei_traits<> specializations.
> 
> Yes, this is a bit complicated, but what is very important here, and
> that we have to better document, is that when you write a generic
> function taking, e.g., a MatrixBase<Derived> object you should really
> honor the nesting type of the Derived class:
> 
> template<typename Derived>
> void foo(const MatrixBase<Derived>& _x)
> {
>    typename Derived::Nested x(_x.derived());
> 
>   // use x safely
> }
> 
> Actually, is you use the argument more than once, you should even use
> the ei_nested<> helper:
> 
> template<typename Derived>
> typename Derived::Scalar foo(const MatrixBase<Derived>& _x)
> {
>    typename ei_nested<Derived,2>::type x(_x.derived());
> 
>    return (x + x.adjoint()).maxCoeff();
> }
> 
> if you don't do so, and call:
> 
> foo(a*b);
> 
> then the expensive product a*b will be computed twice !!
> 
> gael
> 
> On Tue, Jun 29, 2010 at 9:29 PM, Manoj Rajagopalan <rmanoj@xxxxxxxxx> wrote:
> > Hi,
> >
> >  I see typedefs with names like "MatrixTypeNested". What is this nesting
> > concept?
> >
> > thanks,
> > Manoj
> >
> >
> >
> 
> 
> 



Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/