Re: [eigen] inconsistent cwise() support |
[ Thread Index |
Date Index
| More lists.tuxfamily.org/eigen 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)
Cheers,
Jitse