Re: [eigen] inconsistent cwise() support

[ Thread Index | Date Index | More Archives ]

On Tue, 17 Nov 2009, Gael Guennebaud wrote:

I'm thinking that once we'll have a true array support, all this array/cwise stuff is going to be messy. Here I'll assume that a true Array class supporting the standard global math function (e.g., std::sin) is really needed.

I agree that we need an easy way to apply functions like sin coefficient-wise. However, the Array class has the disadvantage that A*B has different meaning depending on whether A and B are Matrix or Array.

To me, that seems a serious issue, but perhaps I'm alone in that opinion. Nevertheless, let me discuss how to mitigate this. When the Array class is proposed, I thought that it was to support the different languages used in different domains (e.g., and rather crudely, theoretical linear algebra for Matrix, data manipulation for Array). A program in the first domain would use exclusively Matrix, and a program in the second domain would use exclusively Array. In that case, confusion is minimal, so I think that would be good programming style.

However, now it's envisaged that Matrix and Array are freely mixed, and if cwise() is abolished it's even necessary to do so. So, the next best thing is to make sure that all your variables are Matrix (supposing you're in the theoretical linear algebra domain). As Gael said, this will lead to code like the following for componentwise multiplication = Hadamard product:

  MatrixXd A = ...
  MatrixXd B = ...
  MatrixXd prod = (A.array() * B.array()).matrix();

Not only is that cumbersome, but I think that it's more difficult to understand than A.cwise() * B.

I think it is also important to acknowledge that the current cwise() API is odd enough to make it hard to learn for new comers. They often have some difficulties to determine where .cwise() is needed (e.g., m1.cwise() * m2.cwise()), and how it propagates (though it does not, of course). For instance, if you look at "m1.cwise() * m2" and you know, or did not understand the prefix concept of .cwise(), then it seems that .cwise propagate to m2.

I agree with this, cwise() is difficult, because of what you call the prefix concept: in A.cwise().max(B), cwise() modifies max, while C++ programmers will think it modifies A. Perhaps a resolution would be to add a cwisemax() function to MatrixBase, so that we would replace A.cwise().max(B) to A.cwisemax(B). Now it's clear that the cwise prefix modifies max.

With componentwise multiplication, that would mean that we replace A.cwise() * B by A.cwisemul(B). It's a pity that we lose the multiplication sign, but I think that we win on clarity: it's hard to misinterpret what A.cwisemul(B) would mean.

With the addition of Array, that leads to the following API. The idea is that a module would use either the left or the right column, but not mix expressions.

                      Matrix A,B          Array A,B
matrix multiply:      A * B               A.matrixmul(B)
c-wise multiply:      A.cwisemul(B)       A * B
matrix exponential:   A.exp()             A.matrixexp()
c-wise exponential:   A.cwiseexp()        A.exp()

The discussion on whether to introduce global functions instead of (or in parallel of) member functions seems to be orthogonal to this, but just for illustration, this is how it would work with global functions.

                      Matrix A,B          Array A,B
matrix multiply:      A * B               matrixmul(A,B)
c-wise multiply:      cwisemul(A,B)       A * B
matrix exponential:   exp(A)              matrixexp(A)
c-wise exponential:   cwiseexp(A)         exp(A)


Mail converted by MHonArc 2.6.19+