Re: [eigen] Eigen Types as Parameters for Functions

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


The current implementation is only a first draft which clearly needs
some improvements.

On Wed, Jun 27, 2012 at 11:14 AM, Christoph Hertzberg
<chtz@xxxxxxxxxxxxxxxxxxxxxxxx> wrote:
> some questions/notes:
> * Does A.row(3); change after calling foo1(A.row(3))? (i.e. is it
>  copied back?) -- you could simply copy it back in the destructor, if
>  match_helper<Derived>::type() is false_type.
>  If you think back-copying causes too much performance penalty, you
>  need to forbid this.
>  Furthermore, it sometimes might be useful to mark Ref as a pure
>  "out-ref" in this case, saving the copy from A.row() to m_object.

That's a very good remark, and currently I don't have a strong opinion
whether we should copy-back or forbid it. The copy-back option is ok
when Ref is used for function arguments and in sequential algorithms,
but very dangerous in all other cases. For instance, I was thinking
about Ref<> inside Eigen itself to reduce template instantiations. In
this case you clearly don't want the copy-back approach.
 On the other hand, I known it would be very convenient for many
users. Proposing both might be overkill.

> * If you pass, for example, a Ref<Vector4d>, don't you have a Vector4d
>  (the m_object member) lying on the stack then? That might cause
>  alignment issues if the stack is not aligned (I did not test it yet).

I don't think that's a problem because if the compiler does not know
how to align the stack then we cannot use aligned objects at all, not
only inside Ref<>.


>
>
> Christoph
>
>
>
>
>
> On 27.06.2012 10:25, Gael Guennebaud wrote:
>>
>> Hi all,
>>
>> I've implemented a solution fully described there:
>> http://eigen.tuxfamily.org/bz/show_bug.cgi?id=481
>>
>> feedback more than welcome!
>>
>> For the lazy ones, here is a self-explanatory example of the proposed
>> solution:
>>
>> #include <iostream>
>> #include <Eigen/Dense>
>> using namespace Eigen;
>>
>> // read-write access to 'a'
>> void foo1(Ref<VectorXf> a) { }
>>
>> // read-only access to 'a'
>> void foo2(Ref<const VectorXf> a) { }
>>
>> int main()
>> {
>>   VectorXf a(10);
>>   const VectorXf& ac(a);
>>   VectorBlock<VectorXf> ab(a,0,3);
>>   MatrixXf A(10,10);
>>   const VectorBlock<VectorXf> abc(a,0,3);
>>
>>   foo1(a);
>>   //foo1(ac);       // does not compile because ac is const
>>   foo1(ab);
>>   foo1(a.head(4));
>>   foo1(abc);
>>   foo1(A.col(3));
>>   foo1(A.row(3));   // copied into a temp because innerstride!=1
>>
>>   foo2(A*A.col(1)); // evaluated into a temp
>>   foo2(ac.head(5));
>>   foo2(ac);
>>   foo2(a);
>>   foo2(ab);
>>   foo2(a.head(4));
>>   foo2(a+a);        // evaluated into a temp
>>
>>   return 0;
>> }
>>
>> cheers,
>> Gaël
>>
>> On Mon, Feb 27, 2012 at 7:44 PM, Gael Guennebaud
>> <gael.guennebaud@xxxxxxxxx> wrote:
>>>
>>> btw,
>>>
>>> this more or less what Christian suggested but more restrictions because:
>>> 1 - the storage order really has to be known at compile time otherwise
>>> we cannot construct an expression
>>> 2 - same for the alignment, especially for small fixed sizes.
>>> Otherwise the alignment is already determined at runtime by checking
>>> the pointers, so no need to specify it.
>>> 3 - a runtime inner-stride can really kill the performance so it is
>>> important to let the user decide whether he can tolerate that or not.
>>> 4 - distinguishing at compile-time between vectors and matrices is
>>> fundamental too
>>>
>>> So if we go for a specific class (not Map) it could had 5 template
>>> parameters:
>>> - typename scalar
>>> - int rows
>>> - int cols
>>> - int storage_order
>>> - int inner_stride = 1
>>>
>>> cheers
>>> gael
>>>
>>> On Mon, Feb 27, 2012 at 7:34 PM, Gael Guennebaud
>>> <gael.guennebaud@xxxxxxxxx> wrote:
>>>>
>>>> Alright, I thought I had already replied to this thread but I haven't
>>>> so here I go.
>>>>
>>>> My general idea to offer the ability to write non template function
>>>> while still offering some flexibility on the input is really simple:
>>>> simply add constructors to Map<> that can take Eigen's objects such as
>>>> Matrix, Block, Map, etc. Then it is up to user to specify the level of
>>>> flexibility he wants for his arguments: dynamic/fixed sizes,
>>>> outer-stride? inner-stride? matrix/vector?
>>>>
>>>> For instance:
>>>>
>>>> void foo(const Map<MatrixXd,0,OuterStride<> >& arg);
>>>>
>>>> should accept any MatrixXd, Matrix4d, Block, Map, etc.
>>>>
>>>> There are some limitations though:
>>>> - No template is possible at all, in particular it is not possible to
>>>> templatize the scalar type, but this is fine to me since if the user
>>>> can tolerate some template parameter then why not allowing everything.
>>>> - The storage order has to be fixed too. I understand a runtime
>>>> storage order might be convenient, but this is really not the Eigen
>>>> philosophy, so this limitation will have to stay.
>>>>
>>>> However, I have question regarding the handling of non compatible
>>>> expressions. For instance what should we do with:
>>>>
>>>> foo(A+B);  // this is a pure expression without direct access
>>>>
>>>> or
>>>>
>>>> MatrixXcf C;
>>>> foo(C.real());  // C.real() has an inner-stride, but foo does not accept
>>>> that
>>>>
>>>> If we stick with Map<> I think that there is only one possible
>>>> solution: compilation error. Then it is up to the user to explicitly
>>>> evaluate it.
>>>>
>>>> Another approach would be to add a variant of Map tailored for this
>>>> use case that would allow to silently evaluate the argument into a
>>>> temporary. This is what happen if the user declare his argument as a
>>>> const MatrixXd&.
>>>>
>>>> Any opinion on which approach we should follow?
>>>>
>>>> cheers,
>>>> gael
>>>>
>>>> On Thu, Jan 26, 2012 at 3:12 PM, Christoph Hertzberg
>>>> <chtz@xxxxxxxxxxxxxxxxxxxxxxxx> wrote:
>>>>>
>>>>> On 26.01.2012 14:50, Benoit Jacob wrote:
>>>>>>
>>>>>>
>>>>>> 2012/1/26 Christoph Hertzberg<chtz@xxxxxxxxxxxxxxxxxxxxxxxx>:
>>>>>>>
>>>>>>>
>>>>>>> On 26.01.2012 14:33, Benoit Jacob wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> 2012/1/26 Christian Seiler<christian@xxxxxxxx>:
>>>>>>>>
>>>>>>>>>  - subclass of MatrixBase, i.e. can be used inside the function
>>>>>>>>> like a
>>>>>>>>>   Matrix object
>>>>>>>>>  - has private members containing the above-mentioned bits of
>>>>>>>>>   information (pointer to first element, dimension, ...)
>>>>>>>>>  - can be implicitly converetd to from any compatible MatrixBase
>>>>>>>>> (i.e.
>>>>>>>>>   foo(mat) and foo(mat.block(...)) can be used directly)
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> Just note that this is only possible for those matrix expressions
>>>>>>>> whose coefficients are stored in memory with a simple enough layout
>>>>>>>> that can be described in terms of strides. That is what we call a
>>>>>>>> "Direct Access" matrix expression; the test is
>>>>>>>> ExpressionType::Flags&
>>>>>>>> DirectAccessBit.
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> Yes, and I would say this would be the main application, if you have
>>>>>>> complicated-enough functions (i.e. where you don't really bother
>>>>>>> about
>>>>>>> evaluating expressions to a temporary if required).
>>>>>>> And more importantly, when having parameters that are actually output
>>>>>>> parameters, you are more or less stuck to direct access types anyway
>>>>>>> --
>>>>>>> and
>>>>>>> this is a very important point, where currently const-correctness is
>>>>>>> ignored
>>>>>>> (almost) entirely.
>>>>>>>
>>>>>>>
>>>>>>>> With that said, yes, we have been considering doing this, that's bug
>>>>>>>> 58:
>>>>>>>> http://eigen.tuxfamily.org/bz/show_bug.cgi?id=58
>>>>>>>>
>>>>>>>> If someone wants to do this and needs mentoring, I should find time
>>>>>>>> to
>>>>>>>> do at least that mentoring.
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> I'd be interested at least to join the "specification committee" ;)
>>>>>>
>>>>>>
>>>>>>
>>>>>> Really, I haven't thought about this in a long time so your thoughts
>>>>>> on this subject are probably a lot fresher than mine. From the top of
>>>>>> my head, the starting point is to look at MapBase (which is inherited
>>>>>> both by Map and by Block<direct access type>) and ask: how much of the
>>>>>> templated stuff there can we replace by runtime variables (data
>>>>>> members), and try to make all direct-access types inherit the
>>>>>> resulting base class (in particular, Matrix should inherit it).
>>>>>
>>>>>
>>>>>
>>>>> Yes, my thoughts were about the same. Actually, the only difference
>>>>> between
>>>>> a Map/Block and a Matrix should be that the Matrix "owns" its memory,
>>>>> i.e.
>>>>> has to deallocate it on destruction (with a special case of fixed size
>>>>> matrices having static memory).
>>>>>
>>>>> Furthermore, if one allows strides in the Matrix-class, some
>>>>> specialized
>>>>> types, such as AlignedVector3 would become just a typedef. Currently,
>>>>> if
>>>>> someone needs an aligned vector7, he'd have to to the same thing again
>>>>> --
>>>>> also it might be interesting to store an aligned 3x3 matrix as the top
>>>>> 3
>>>>> rows of a 4x3 matrix allowing a much more efficient matrix-vector
>>>>> product.
>>>>>
>>>>>
>>>>> Christoph
>>>>>
>>>>>
>>>>>
>>>>> --
>>>>> ----------------------------------------------
>>>>> Dipl.-Inf. Christoph Hertzberg
>>>>> Cartesium 0.051
>>>>> Universität Bremen
>>>>> Enrique-Schmidt-Straße 5
>>>>> 28359 Bremen
>>>>>
>>>>> Tel: (+49) 421-218-64252
>>>>> ----------------------------------------------
>>>>>
>>>>>
>>
>
>
> --
> ----------------------------------------------
> Dipl.-Inf. Christoph Hertzberg
> Cartesium 0.049
>
> Universität Bremen
> Enrique-Schmidt-Straße 5
> 28359 Bremen
>
> Tel: +49 (421) 218-64252
> ----------------------------------------------
>
>
>
>



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