Re: [eigen] On a flexible API for submatrices, slicing, indexing, masking, etc.

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




On Sun, Jan 8, 2017 at 9:01 AM, Gael Guennebaud <gael.guennebaud@xxxxxxxxx> wrote:


On Fri, Jan 6, 2017 at 11:46 PM, Yuanchen Zhu <yuanchen.zhu@xxxxxxxxx> wrote:
Dude I don't know... This is a bit too fancy for me. So now A(3 * any_mat + 4) actually means A(1), A(4), A(7) ...?

To write A(seq(4, 10, 3)), you cannot just write A(4 + 3 * any_mat <= 10), but you need to write  A(4<= 4 + 3 * any_mat <= 10).


Hopefully it is more consistent now.


​It is consistent now, though I still find it a bit weird. But my eyes are probably biased at this point, so I wonder how others think about it.

Another remaining issue is that, as I said, all the example here and in the wiki basically assume step > 0. If increment d is dynamic and can be either positive or negative, you have to write something like 

A(sgn(a) <= sgn(d)(4 + d * anyZ) <= sgn(b))

This is just inferior to just

A(seq(a, b, d))

or 

A(a+iota*d | till(b)) 
 

2) Note that the 'classic' seq function has some caveats too: http://eigen.tuxfamily.org/index.php?title=Working_notes_-_Indexing%2B%2B#Caveats

In short the 'first' and 'last' inclusive bounds are not always symmetric.

​True. 'last' is not a good name. Maybe 'to' or 'till' are better alternatives. But I do think people already accepted and internalized this asymmetry. They don't expect the sequence to end exactly at 'last' when |incr|>1. This also seems to indicate the _expression_ A(a+iota*d | till(b)) makes sense. It makes clear the asymmetry between a and b and announce the semantics of b clearly.
 

3) Before pushing too much towards fancy syntax, I would propose to push further the current seq/seqN paradigms my making them ArrayXi alike. This way we can seamlessly integrate the 'APL' approach and perhaps even cover some aspect of the filtering approach: http://eigen.tuxfamily.org/index.php?title=Working_notes_-_Indexing%2B%2B#Generalization

​This sounds reasonable. BTW, I think seq should just return a seqN with N calculated based on (start, bounds, and incr). So essentially there is one datatype, the arithmetic progression, AP = seqN, parameterized by {start, len, incr}.  Both seq and seqN returns an AP. All three parameters can be static or dynamic (if optimization makes sense). len can be either finite or infinite. When an AP is finite, it also conforms to the concept of ArrayX<Eigen::Index>. Then later on, iota also returns an AP as well. Later we overload + and * and |  and others on the AP if the preserve the AP property, e.g., AP(a, N, d) * i = AP(a*i, N, d*i), AP(a, N, d) + x = AP(a + x, N, d), AP(a, N, d) | till(b) = AP(a, min(N, some formula(a, d, b)), d), and so on. But I'm probably getting ahead of myself at this point.

As a side note, like you said in the the wiki, there is a similarity between the classic chaining using dot (v.f(..).g(...)) and chaining use pipe (v | f | g). I haven't personally implemented any pipe based syntax so I am not yet aware of its more nuanced implementational disadvantages. However, on top of my head, two key advantages of '|' esp. in the context of Eigen are:

- '|' is overloadable but '.' is not. With '.', you have to implement all available functions in the base type of v.
- '|' supports something like v | f<N>, whereas with '.', you have to write v.f<N>(). Eigen can make good use of certain compile-time static parameters.
 

gael
 

 

And yes, a<=any_nat does not return a boolean or a vector of booleans, but once put using this new wording, how could it be?


gael

 
A(2*iota(5) ) // even numbers up to 8
A(3+2*iota(all-2) ) // 3,5,7,… up to N-2
A(all - iota(5) ) // N-1,N-2,..,N-5
...

where bound checking is meant to follow Eigen habits ( eg. hard error if compile time, assert on runtime )







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