This bugzilla service is closed. All entries have been migrated to https://gitlab.com/libeigen/eigen

Bug 112

Summary: We need a true Identity expression
Product: Eigen Reporter: Gael Guennebaud <gael.guennebaud>
Component: Core - generalAssignee: Nobody <eigen.nobody>
Status: DECISIONNEEDED ---    
Severity: Internal Design CC: benjamin, chtz, gael.guennebaud, jacob.benoit.1
Priority: Normal    
Version: 3.0   
Hardware: All   
OS: All   
Whiteboard:
Bug Depends on: 99    
Bug Blocks: 610, 1608    

Description Gael Guennebaud 2010-11-08 13:43:49 UTC
This is a proposal for a pure Identity expression which would not be based on CwiseNullary anymore (this a really inefficient way of implementing Identity).

The Identity object should be generic, not limited to DenseObject. It should have generic operator* returning its argument.

One question is do we want to support rectangular Identity objects? Currently we do, but is there any use cases? If so, operator* would not always return its argument directly but a sub block. This means that we should be able to specify at compile time the Identity object is squared even if its size is dynamic. Perhaps two objects: Identity and RectangularIdentity.

Implementation wise, DiagonalMatrixBase is a good start for the square case.

Regarding sizes, it would be very convenient to be able to instantiate some objects without specific size. This concern Identity of course, but also all CwiseNullary expressions. Indeed, in many (all ?) cases you don't have to specify the dimensions of such kind of objects which can be deduced from the context. To go even further, at least in the case of Identity, the scalar type is useless too, so we could even have a unique Identity object with no size and no scalar type. Dimensions can be needed only if you want a rectangular identity object, and in this case RectangularIdentity<> would do the job.

So far so good, but of course the big issue now is how to implement that without intrusive changes everywhere in our traits class...
Comment 1 Benoit Jacob 2010-11-08 18:39:30 UTC
(In reply to comment #0)
> This is a proposal for a pure Identity expression which would not be based on
> CwiseNullary anymore (this a really inefficient way of implementing Identity).

Agree. Notice that we already bypass it in assignment to get good performance.

> 
> The Identity object should be generic, not limited to DenseObject. It should
> have generic operator* returning its argument.

Right

> 
> One question is do we want to support rectangular Identity objects? Currently
> we do, but is there any use cases?

Good question :) I have a use case in JacobiSVD when initializing thin unitaries: I assign a rectangular identity to them and apply the QR householderSequence to that.

> If so, operator* would not always return its
> argument directly but a sub block. This means that we should be able to specify
> at compile time the Identity object is squared even if its size is dynamic.
> Perhaps two objects: Identity and RectangularIdentity.

I like this, but this would break current API like MatrixXf::Identity(2,3).

Maybe it's not that bad that operator* returns a block expression. We could say it's compensated by having only one kind of Identity. If you don't like this, you can fix this before the 3.0-beta3 release by tightening the static Identity() methods to require square sizes and introduce RectangularIdentity behaving like current Identity().


> 
> Implementation wise, DiagonalMatrixBase is a good start for the square case.
> 
> Regarding sizes, it would be very convenient to be able to instantiate some
> objects without specific size. This concern Identity of course, but also all
> CwiseNullary expressions. Indeed, in many (all ?) cases you don't have to
> specify the dimensions of such kind of objects which can be deduced from the
> context. To go even further, at least in the case of Identity, the scalar type
> is useless too, so we could even have a unique Identity object with no size and
> no scalar type. Dimensions can be needed only if you want a rectangular
> identity object, and in this case RectangularIdentity<> would do the job.
> 
> So far so good, but of course the big issue now is how to implement that
> without intrusive changes everywhere in our traits class...

I agree it would be quite elegant to have such sizeless objects!

But if such sizeless objects were still expressions, we'd need a very deep generalization of our current notion of expression (some expression would have unspecified sizes and in particular could not be evaluated into plain objects). I think it's too late for this.

So I would suggest that such sizeless objects would be just special objects, not expressions. This approach can be easily implemented at any time without breaking/changing anything. So we could do:

struct IdentityObj_t{};

namespace { IdentityObj_t IdentityObj; }

and add constructors, operator=, etc, taking a dummy IdentityObj_t argument.

So users could do:

  Matrix3f m = Eigen::IdentityObj;  // ok
  MatrixXf m = Eigen::IdentityObj;  // failed assert: size unspecified
Comment 2 Christoph Hertzberg 2013-10-18 15:51:45 UTC
W.r.t sizeless objects/square identities: How about introducing special dimension values for these cases similar to Eigen::Dynamic?, E.g. Eigen::Auto=-2 and Eigen::Square=-3, that would make Identity<Auto, Auto> a rectangular matrix that always fits its requirements, Identity<Auto,Square> a square matrix fitting its requirements, Identity<Dynamic,Square> a squared identity matrix where the size needs to be specified at runtime.

The Square size might be useful for general matrices/Maps as well: E.g., Matrix<double, Dynamic, Square> is guaranteed to be a squared matrix (this would also save to store one dimension).
Comment 3 Christoph Hertzberg 2014-02-10 16:11:50 UTC
We may also want a Zero and a Ones object.

One design decision is what this shall do:
  Array22d A = Eigen::IdentityObj;
Set A to [1,0;0,1], to [1,1;1,1] or generate a compile error? I'm tending to the latter (maybe EIGEN_CANNOT_ASSIGN_STATIC_MATRIX_EXPRESSION_TO_ARRAY), with the alternatives:
  Array22d A = Eigen::IdentityObj.array(); // A = [1,0; 0,1]
and:
  Array22d A = Eigen::OnesObj;  // A=[1,1; 1,1]

Eventually, I don't think we need the Obj postfixes.

And we could also make static methods like these:
namespace Eigen {
static IdentityObj Identity();
template<int rows, int cols>
static IdentityObjRect<rows,cols> Identity();
static IdentityObjRect<Dynamic,Dynamic> Identity(int rows, int cols);
// etc
}
Comment 4 Gael Guennebaud 2014-02-14 15:49:10 UTC
I also think Array22d A = Eigen::IdentityObj; should not be allowed.

I also had in mind an "auto" dimension, and "auto" storage order.

For square matrices, this is probably less critical, so I'd rather ignore it in a first place because handling dynamic versus fixed size is already very complex at some places. Adding "auto" dimension/storage will make thing much worse, and if we add one more square option I'm afraid my brain will collapse ;)
Comment 5 Nobody 2019-12-04 09:58:51 UTC
-- GitLab Migration Automatic Message --

This bug has been migrated to gitlab.com's GitLab instance and has been closed from further activity.

You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.com/libeigen/eigen/issues/112.