[ Thread Index |
Date Index
| More lists.tuxfamily.org/eigen Archives
]
- To: eigen@xxxxxxxxxxxxxxxxxxx
- Subject: Re: [eigen] FFT update
- From: Benoit Jacob <jacob.benoit.1@xxxxxxxxx>
- Date: Sat, 23 Jan 2010 01:10:15 -0500
- Dkim-signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:mime-version:received:in-reply-to:references :date:message-id:subject:from:to:content-type; bh=ULtLdS8wj3lpRb3bAqiceLl+PsdLC+8kokYIGV23lD4=; b=A3ECd0EHgbZWNWTY2XfuFUdD67WCc0xCcD5t93FYcNRIefus5U9zZ66nP5cykjFXXu ls+igAqV0Vd2wzu1eOlx3yiciFxZ6yy8M/nJtynY13ZMfLIDJuXiD+rUNG9BPtbCAB9R gQClygPig+1QG3fbv/ZqQ+AWGBachdI13fJ3s=
- Domainkey-signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :content-type; b=Q2IBf8Awld2KKwq67N9kyy95sFJbEMZu8wVXXz351E24SM80f0jz4dWp8F23cQVoZu Hw6LjbDIaJ29+ISiA8+G30mZbJos7OWtSlHb0Ualw4AQbE/3Yx+MuKHCQkmyEw+J1z8G Keeu3tvomp96PTnl96BEk+k/kBmZbfgc2d/1I=
Hi,
Here's an example: see attached file.
Enjoy!!
Benoit
2010/1/22 Benoit Jacob <jacob.benoit.1@xxxxxxxxx>:
> ok let me write an example...
>
> 2010/1/22 Jitse Niesen <jitse@xxxxxxxxxxxxxxxxx>:
>> On Fri, 22 Jan 2010, Benoit Jacob wrote:
>>
>>> Our "policy" is now:
>>> - try to return by value, optimizing with the ReturnByValue class,
>>> wherever possible;
>>> - otherwise you can just use references.
>>
>> Can somebody please explain how to use the ReturnByValue class?
>> Or should I just extrapolate from the source code?
>> I could not find any documentation.
>>
>> Jitse
>>
>>
>>
>
/*** Bon appetit bien sur! With chef Benoit.
***
*** Today we'll learn to cook a closure-like function with optimized returning-by-value
*** thanks to the ReturnByValue class.
***/
#include <Eigen/Core>
using namespace Eigen;
using namespace std;
// here is a function naively returning a matrix or array by value.
template<typename Derived>
typename Derived::PlainMatrixType // get the plain matrix/array type corresponding to this expression type.
// yes, this typedef is from the days when we didn't have Array.
rotate_naive(const DenseBase<Derived>& src)
{
EIGEN_STATIC_ASSERT_VECTOR_ONLY(Derived)
typename Derived::PlainMatrixType result(src.size());
const int n = src.size()-1;
result.tail(n) = src.head(n);
result.coeffRef(0) = src.coeff(n);
return result;
}
// now the problem is that when we do:
// y = rotate_naive(x)
// the result is first computed in a temporary variable that is then copied into y.
// In order to optimize that, we use the ReturnByValue class. It is a return-value-proxy, or a
// "closure" if you want. The idea is that the rotate() function should not return the whole result
// array/matrix, instead it should return a lightweight ReturnByValue object that only describes
// the operation, and the operation is only done by the subsequent "=". So this is lazy evaluation.
// The reason why this is nontrivial to implement is that the user might instead be doing something like
// rotate(x) + something_else
// So the ReturnByValue class is a real expression in the sense of Eigen's expression templates, and
// upon nesting into a larger expression, it immediately evaluates its result.
// The ReturnByValue class is not used directly, instead you derive it to specialize it to your problem.
// So let's define a derived class rotate_retval.
// First we declare it.
template<typename Derived> struct rotate_retval;
// Then we specialize ei_traits for it. After all, it's an expression class like any other.
// Don't forget that ei_traits belongs to namespace Eigen!
namespace Eigen {
template<typename Derived>
struct ei_traits<rotate_retval<Derived> >
{
typedef typename Derived::PlainMatrixType ReturnMatrixType;
// get the plain matrix/array type corresponding to this expression type.
// yes, this typedef is from the days when we didn't have Array. Need to rename it.
};
}
// Now we can define the rotate_retval class. It inherits ReturnByValue<rotate_retval>:
// yes, this is again the famous "curiously recursive template pattern".
// The ReturnByValue base class is an expression class in the sense of expression templates:
// it inherits DenseBase. As such it brings us all the expression template facilities that we need,
// so we only have minimal stuff to implement here.
template<typename Derived> struct rotate_retval
: public ReturnByValue<rotate_retval<Derived> >
{
// The constructor is all what's called by the rotate() function that we implement below.
// All it does is store a reference to the source matrix/array.
rotate_retval(const Derived& src) : m_src(src) {}
// almost all we have to do is explain how the "rotate" expression will be evaluated. This is where
// the implementation goes! This time, the return value is a reference, Dest& dst, so we don't
// actually return the result by value.
template<typename Dest> inline void evalTo(Dest& dst) const
{
dst.resize(m_src.size());
const int n = m_src.size()-1;
dst.tail(n) = m_src.head(n);
dst.coeffRef(0) = m_src.coeff(n);
}
// oh right and we must also say how many rows/columns our expression has. This is needed to be
// able to create the destination matrix/array when we need to evaluate into a temporary.
int rows() const { return m_src.rows(); }
int cols() const { return m_src.cols(); }
protected:
const Derived& m_src;
};
// Finally we can implement our rotate() function.
template<typename Derived>
rotate_retval<Derived> rotate(const DenseBase<Derived>& src)
{
// this is the right place for assertions, so errors are caught are the place where they exist.
EIGEN_STATIC_ASSERT_VECTOR_ONLY(Derived)
return rotate_retval<Derived>(src.derived());
// do you know how I lost 30 minutes debugging this shit? I forgot the derived() in the above line.
// So my src object of type DenseBase<Derived> was being deep-copied into a temporary of type Derived.
// The temporary then died, and I was reading undefined values. So, don't forget the derived().
// Thanks for listening.
}
/*** Bake in GCC for 40 minutes... ***/
// Now let's taste it:
int main()
{
RowVector3f v(1,2,3);
cout << "Here's v: " << v << endl;
// Test rotate_naive()
cout << "Here's rotate_naive(v): " << rotate_naive(v) << endl;
// Test rotate()
cout << "Here's rotate(v): " << rotate(v) << endl;
// Wait! You ask. Since rotate(v) returns an expression of type rotate_retval that can only be evaluated
// and on which we may not call coeff(), how was it possible to print it? Didn't it try to call coeff()
// on it to print the coeffs? No! Because the first thing that cout<< does, is to _nest_ the expression
// that is passed to it. ReturnByValue says "when nesting me, you must first evaluate me". So the expression
// gets evaluated into a temporary before being printed.
// But this would crash (infinite recursion):
// rotate(v).coeff(0)
// or
// rotate(v)(0).
// Just to show off, let's nest rotate() into a bigger expression:
cout << "Here's 2*rotate(v): " << 2*rotate(v) << endl;
// see? no problemo.
}
// Bon appetit bien sur!!!