Re: [eigen] Question about the class hierarchy

[ Thread Index | Date Index | More Archives ]

2010/6/30 Hauke Heibel <hauke.heibel@xxxxxxxxxxxxxx>:
> On Wed, Jun 30, 2010 at 6:07 AM, Manoj Rajagopalan <rmanoj@xxxxxxxxx> wrote:
>> 1.   Aren't Array and Matrix independent *concepts*? Therefore, a dense matrix
>> expression, which shares traits with an array expression should inherit both
>> an Array base class and a Matrix base class simultaneously using multiple
>> inheritance as opposed to the linearized inheritance in Eigen.
> We try hard to prevent multiple-inheritance since we have encountered
> examples where MSVC fails to perform empty base class optimization
> which is crucial for our fixed size types.
> We really want that
> sizeof( Matrix<double,2,2> ) = 4*sizeof(double)
>> 2.   Other matrices should inherit this Matrix base class. Then all matrices
>> can be handled with matrix semantics using one base class without having to
>> worry about intervening array semantics - this is what makes me most
>> uncomfortable. For example, take solve() methods which accept const
>> matrix-expressions as arguments. Therefore, they should accept
>> DiagonalMatrix/Wrapper, SparseMatrix etc. If the solve() prototype is
>> declared with MatrixBase<OtherDerived> const& as arg then we rule out this
>> possibility. On the other hand, if we declare it with EigenBase<...> const&
>> as arg, then it also allows Array expressions to be passed. the latter is not
>> a problem if we allow it and warn the user to guard against this caveat but
>> still conceptually Arrays are distinct from Vectors/Matrices ...
> Maybe, looking at this document helps you a bit:
> What used to be called in this document AnyMatrixBase is now our EigenBase.

Unfortunately, however, it is a bit outdated in several ways. In the
line of Gael's email from yesterday on ei_nested, let's continue
writing some devel docs material ... ;-)

Explanation of our various classes and how they fit in the hierarchy:

The 5 dense classes everyone has to know:

* Matrix means plain dense matrix. If m is a Matrix, then, for
instance, m+m is no longer a matrix, it is a "matrix expression".

* MatrixBase means dense matrix expression. This means that a
MatrixBase is something that can be added, matrix-multiplied,
LU-decomposed, QR-decomposed... All matrix expression classes,
including Matrix itself, inherit MatrixBase.

* Array means plain dense array. If x is an Array, then, for instance,
x+x is no longer an array, it is an "array expression".

* ArrayBase means dense array expression. This means that an ArrayBase
is something that can be added, array-multiplied, and on which you can
perform all sorts of array operations... All array expression classes,
including Array itself, inherit ArrayBase.

* DenseBase means dense (matrix or array) expression. Both ArrayBase
and MatrixBase inherit DenseBase. DenseBase is where go all the
methods that apply to dense expressions regardless of whether they are
matrix or array expressions. For example, the block(...) methods are
in DenseBase.

More internal, less interesting classes:

* DenseStorageBase means dense (matrix or array) plain object, i.e.
something that stores its own dense array of coefficients. This is
where, for instance, the resize() methods go. DenseStorageBase is
inherited by Matrix and by Array. But above, we said that Matrix
inherited MatrixBase and Array inherited ArrayBase. So does that mean
multiple inheritance? No, because DenseStorageBase _itself_ inherits
MatrixBase or ArrayBase depending on whether we are in the matrix or
array case. When we said above that Matrix inherited MatrixBase, we
omitted to say it does so indirectly via DenseStorageBase. Same for

* DenseCoeffsBase means something that has dense coefficient
accessors. It is a base class for DenseBase. The reason for
DenseCoeffsBase to exist is that the set of available coefficient
accessors is very different depending on whether a dense expression
has direct memory access or not (the DirectAccessBit flag). For
example, if x is a plain matrix, then x has direct access, and
x.transpose() and x.block(...) also have direct access, because their
coeffs can be read right off memory, but for example, x+x does not
have direct memory access, because obtaining any of its coefficients
requires a computation (an addition), it can't be just read off

* EigenBase means anything that can be evaluated into a plain dense
matrix or array (even if that would be a bad idea). EigenBase is
really the absolute base class for anything that remotely looks like a
matrix or array. It is a base class for DenseCoeffsBase, so it sits
below all our dense class hierarchy, but it is not limited to dense
expressions. For example, EigenBase is also inherited by diagonal
matrices, sparse matrices, etc...

So the inheritance graphs look like this:

For Matrix:

  <--DenseCoeffsBase<Matrix> (direct access case)
        <--DenseStorageBase<Matrix> (matrix case)

For Array:

  <--DenseCoeffsBase<Array> (direct access case)
        <--DenseStorageBase<Array> (array case)

For some other Matrix expression class SomeMatrixXpr:

  <--DenseCoeffsBase<SomeMatrixXpr> (direct access or no direct access case)

For some other Array expression class SomeArrayXpr:

  <--DenseCoeffsBase<SomeArrayXpr> (direct access or no direct access case)

Finally here's an example of something that isn't a dense expression:
a diagonal matrix.


Hope that helps!

>> 3.   If the Array base and Matrix base must inherit EigenBase then this
>> inheritance must be virtual so that there is exactly one copy of EigenBase in
>> the derived object. virtual inheritance works with templates at least with
>> gcc 4.2 on my linux machine.
> I don't quite understand this? The EigenBase has zero size. No
> members, no virtual functions. Having even a single virtual function
> in EigenBase is not an option because of the sizeof constraint I told
> you about before.
>> 4. EigenBase seems to contain member functions that internal documentation
>> warns against using. Equivalent code seems to be present in eponymous member
>> function definitions of the derived classes. Then, if this makes the
>> EigenBase member definitions redundant and we can remove them, this leaves
>> very little in EigenBase itself - just a few methods like rows(), cols(),
>> size() etc. Are we keeping these redundant definitions just so that we can
>> avoid re-declaring all of these methods in the derived classes?
> Yes, IIRC that is the reason. We would like to absolutely minimize
> code redundancy and it comes at no cost since the compiler will
> anyways perform EBO.
> - Hauke

Mail converted by MHonArc 2.6.19+