This forum has been archived. All content is frozen. Please use KDE Discuss instead.

Conversion to and from scalar types

Tags: None
(comma "," separated)
Ian Mackenzie
Registered Member
Posts
15
Karma
0

Conversion to and from scalar types

Thu May 06, 2010 3:04 am
I've been working on ways to try to get 1x1 (fixed size) matrix types to convert to and from the corresponding scalar type...in my case, the objective was to allow generalized function objects, but I think it could be generally useful. I basically wanted to be able to write things like
Code: Select all
template<class Type, int rows, int cols>
struct Twice {
    Matrix<Type, rows, cols> operator()(const Matrix<Type, rows, cols>& argument) {
        return 2 * argument;
    }
};

and have it work for both scalars and vector types, e.g.,
Code: Select all
typedef Matrix<double, 1, 1> Scalar1d;

Twice<double, 1, 1> twice1d;
Twice<double, 3, 1> twice3d;

double a = twice1d(Scalar1d(1.0));
Scalar1d b = twice1d(1.0);

Vector3d c = twice3d(Vector3d(1.0, 1.0, 1.0));

plus be able to do fun stuff like
Code: Select all
Vector3d v(1.0, 2.0, 3.0);
double a = v.row(1);


After trying a bunch of different approaches, I think I eventually managed to find a reasonably elegant solution using EIGEN_MATRIXBASE_PLUGIN, EIGEN_MATRIX_PLUGIN, and a bit of template trickery. My EIGEN_MATRIXBASE_PLUGIN looks like
Code: Select all
EIGEN_STRONG_INLINE
operator typename ScalarType<SizeAtCompileTime, Scalar>::Type() {
    return derived().coeff(0);
}

EIGEN_STRONG_INLINE
Derived& operator=(const typename ScalarType<SizeAtCompileTime, Scalar>::Type& value) {
    derived().coeffRef(0) = value;
}

my EIGEN_MATRIX_PLUGIN looks like
Code: Select all
EIGEN_STRONG_INLINE
Matrix(const typename ScalarType<SizeAtCompileTime, Scalar>::Type& value) {
    m_storage.data()[0] = value;
}

and before including any Eigen headers I define
Code: Select all
namespace Eigen
{
    struct DummyScalar
    {
    };
   
    template<int size, class Scalar>
    struct ScalarType
    {
        typedef DummyScalar Type;
    };
   
    template<class Scalar>
    struct ScalarType<1, Scalar>
    {
        typedef Scalar Type;
    };
}


The end effect is that a MatrixBase of size 1 has a conversion operator to Scalar, and any other sized MatrixBase class has a conversion operator to DummyScalar (which is basically not a conversion operator at all, because you can't do anything with a DummyScalar). Similarly, only MatrixBase objects of size 1 can have Scalar objects assigned to them, and only Matrix objects of size 1 can be implicitly constructed from Scalar objects. Note that none of the functions involving DummyScalar objects would actually compile, since for example the line
Code: Select all
m_storage.data()[0] = value;

wouldn't make sense when 'value' is a DummyScalar, but this is OK because template class members that are never used don't have to be able to compile.

Thoughts? My (admittedly very few) initial tests seem to show that this works, but I'd love to hear about cases where it could fail or cause other problems. Also, are there ways to try to eliminate any overhead resulting from wrapping scalars in 1x1 matrices, i.e., ways to make sure the resulting code compiles right down to scalar operations?
Ian Mackenzie
Registered Member
Posts
15
Karma
0
Oops - just realized operator= doesn't really work, since it can't be inherited (unlike conversion operators). I got assignment to work by some hackery in my EIGEN_MATRIXBASE_PLUGIN, redefining the EIGEN_INHERIT_ASSIGNMENT_OPERATORS so that any subclass that used the macro also got an assignment operator:
Code: Select all
#undef EIGEN_INHERIT_ASSIGNMENT_OPERATORS
#define EIGEN_INHERIT_ASSIGNMENT_OPERATORS(Derived) \
EIGEN_INHERIT_ASSIGNMENT_OPERATOR(Derived, =) \
EIGEN_INHERIT_ASSIGNMENT_OPERATOR(Derived, +=) \
EIGEN_INHERIT_ASSIGNMENT_OPERATOR(Derived, -=) \
EIGEN_INHERIT_SCALAR_ASSIGNMENT_OPERATOR(Derived, *=) \
EIGEN_INHERIT_SCALAR_ASSIGNMENT_OPERATOR(Derived, /=) \
EIGEN_STRONG_INLINE Derived& operator=(const Scalar& value) {this->coeffRef(0) = value; return *this;}

but it seems pretty ugly (I also don't know why, but trying to use EIGEN_INHERIT_SCALAR_ASSIGNMENT_OPERATOR(Derived, =) doesn't work).
User avatar
ggael
Moderator
Posts
3447
Karma
19
OS
we are currently talking about such a feature on the ML:

ok I've a working version of that:

Matrix<float,1,1> mat;

mat = 1;
float x = mat + 1
std::sin(mat);

but there is a quite big downside:

mat * 2

does not compile anymore because there is an ambiguity which has to be resolved by doing either:

mat * Scalar(2);
mat * 2.f;
Scalar(mat) * 2;

Maybe the worst thing is that such an ambiguity will show up for 1x1 objects only... so think about general template code which compile fine for all objects except 1x1. Since the later ones are likely not be well tested....

I'm tempted to postpone this feature and/or enable this conversion for inner products only which are less likely to trigger such issues. Also for other use cases there is the class Array which is supposed to be explicitly fully compatible with scalars.

gael


Have you looked at the array class ? isn't it enough for your needs ?
Ian Mackenzie
Registered Member
Posts
15
Karma
0
Yeah, I ran into the ambiguous-operator problem as well...I ended up making a specialization of the Matrix template for 1x1 matrices and did NOT derive it from MatrixBase (instead just implementing all the functions separately, which is quite trivial when the matrix is a scalar!). The disadvantage is that generic functions could not be written to take a MatrixBase<Derived>.

That worked, but all this and other related template magic in my code made my compile times shoot through the roof (my fault, not Eigen's). So instead of having templated functions that could take matrices of different sizes as well as scalar types, I switched to just using dynamic-sized matrices and explcitly converting to and from scalar types when necessary. I added a .scalar() function to MatrixBase which just does an assert and then calls operator()(0, 0); I was also going to add a static Scalar() function, but then realized that would clash with the template parameter.

The Array class looks very promising, although I was going to wait until the first beta before switching over to Eigen 3...


Bookmarks



Who is online

Registered users: Bing [Bot], Google [Bot], Sogou [Bot]