Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fixed size vectors #26

Merged
merged 10 commits into from
Jul 23, 2019
Merged

Conversation

ahoarau
Copy link
Contributor

@ahoarau ahoarau commented Nov 21, 2017

This PR adds eigen_vector2,eigen_vector3 and eigen_vector4 so we can use them in scripting.
Implementation is a bit ugly, but I'd like also to do the same for matrix2d,3d,4d, the default in eigen.

To test it :

import("eigen_typekit")

var eigen_vector2 a
var eigen_vector2 b = array(1.,2.)

var eigen_vector3 c
var eigen_vector3 d = array(1.,2.,3.)

var eigen_vector4 e
var eigen_vector4 f = array(1.,2.,3.,4.)

Gives :

Deployer [S]> import("eigen_typekit")
 = true                

Deployer [S]> 
Deployer [S]> var eigen_vector2 a
 =                     0                   
0                   

Deployer [S]> var eigen_vector2 b = array(1.,2.)
0.905 [ Warning][Logger] Conversion from array to eigen_vector2
 =                     1                   
2                   

Deployer [S]> 
Deployer [S]> var eigen_vector3 c
 =                     0                   
0                   
3.84581e-315        

Deployer [S]> var eigen_vector3 d = array(1.,2.,3.)
0.910 [ Warning][Logger] Conversion from array to eigen_vector3
 =                     1                   
2                   
3                   

Deployer [S]> 
Deployer [S]> var eigen_vector4 e
 =                     9.75025e+199        
5.55604e+180        
1.27735e-152        
3.68066e+180        

Deployer [S]> var eigen_vector4 f = array(1.,2.,3.,4.)
2.325 [ Warning][Logger] Conversion from array to eigen_vector4
 =                     1                   
2                   
3                   
4     

Copy link
Member

@meyerj meyerj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some minor comments, but otherwise looks good to me. Thanks for your contribution. Indeed it might be useful to add some matrix types in the future.

eigen_typekit/eigen_typekit.cpp Outdated Show resolved Hide resolved
eigen_typekit/eigen_typekit.cpp Outdated Show resolved Hide resolved
eigen_typekit/eigen_typekit.cpp Outdated Show resolved Hide resolved
@ahoarau
Copy link
Contributor Author

ahoarau commented Nov 30, 2017

Isn't there any way to "hide" the fixed-sized vectors into regular orocos eigen_vectors ?
Or maybe a simple way to define custom fixed sizes vectors/matrix in our own projects ?
(ex defining a twist would require a 6D vector..)

@meyerj
Copy link
Member

meyerj commented Nov 30, 2017

I would not know how to generate a typekit that supports a dynamic number of types or variable template arguments. The type system of RTT is based on the idea that each type can be used to look up a TypeInfo instance (if a typekit has been loaded) and the TypeInfo provides various type-specific methods that need to be compiled for each possible type or template instantiation.

What is possible is to expose the template declarations of VectorTypeInfo<T> and the other methods in a header file. This way you could register custom types in your own Orocos executable, another typekit plugin or in the constructor of a component that uses these types, for example:

typedef Eigen::Vector<double, 6, 1> Vector6d;
using namespace RTT::types;
if (!Types()->getTypeInfo<Vector6d>()) {
  Types()->addType(new VectorTypeInfo<Vector6d>("eigen_vector6"));
}

The extern template declarations and explicit instantiations are not required and only an optimization on compile time and code size.

But the VectorTypeInfo<T> and related functions should not be exposed as part of the Eigen namespace and deserve their own namespace eigen_typekit or similar instead. There might be some difficulties related to argument depending lookup. For example, the istream streaming operators should be defined in the RTT::types namespace so that they will be found by the implementation of TypeStreamSelector without having to "pollute" the Eigen namespace. An alternative to define streaming operators is to specialize this template class for Eigen types and don't use the streaming operators at all.

That reminds me that I once created a patch for RTT based on Boost's enable_if and operator type traits, that removed the need to specify explicitly whether streaming operators are defined for a certain type and also made the istream operator optional...

+ Vector6d/Matrix6d
@ahoarau
Copy link
Contributor Author

ahoarau commented Dec 1, 2017

Ok I took care of your comments, and also took the liberty of adding the Vector6d and Matrix6d to the set.

import("eigen_typekit")

var eigen_vector2 a
var eigen_vector2 b = array(1.,2.)

var eigen_vector3 c
var eigen_vector3 d = array(1.,2.,3.)
c = array(5.,4.,3.,2.,1.,0.)


var eigen_vector4 e
var eigen_vector4 f = array(1.,2.,3.,4.)


var eigen_vector3 e1 = eigen_vector3(1,11)
var eigen_vector3 e2 = eigen_vector3(123,2)


var eigen_matrix m1 = eigen_matrix(10,11)
var eigen_matrix4 m2 = eigen_matrix4(123,2)

Don't you think it'd be better to initialize the vectors/matrices to zero when resizing ?

@meyerj
Copy link
Member

meyerj commented Jul 1, 2019

Don't you think it'd be better to initialize the vectors/matrices to zero when resizing ?

Indeed, this might be helpful. Normally Eigen does not initialize elements of vectors and matrices for performance reasons. But in all cases where the constructors provided by Orocos typekits are invoked performance is probably not the primary concern.

Especially for variable-sized vectors and matrices it would be useful to initialize elements, or to provide alternative constructors that initialize all elements (like vector_index_value_constructor for Eigen vectors).

Without an operator to access matrix elements (see #23, or at least an unary index in the default storage order column-major) and without the implementation of at least the common operators for addition and multiplication, Eigen types are not very useful in Orocos scripting anyway.

Copy link
Member

@meyerj meyerj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Constructors with size arguments should be removed for fixed-size Eigen types.
  • Constructors from an array should check input sizes before the assignment, issue a warning and default-initialize the remaining arguments. Initialization of any fixed-size vector or matrix from an empty array could be used to default-initialize all elements without emitting a warning.
  • Add matrix_size_value_constructor (for variable-sized matrices) and matrix_value_constructor (for fixed-size matrices) to initialize all elements with the given value.
  • Rename vector_index_array_constructor to vector_array_constructor (read: construct vector from array)
  • Rename *_index_constructor classes to *_size_constructor (read: construct variable-sized vector/matrix by size)

RTT::types::Types()->type("eigen_vector2")->addConstructor(types::newConstructor(vector_index_constructor<Vector2d>()));
RTT::types::Types()->type("eigen_vector3")->addConstructor(types::newConstructor(vector_index_constructor<Vector3d>()));
RTT::types::Types()->type("eigen_vector4")->addConstructor(types::newConstructor(vector_index_constructor<Vector4d>()));
RTT::types::Types()->type("eigen_vector6")->addConstructor(types::newConstructor(vector_index_constructor<Vector6d>()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not make sense to add vector_index_constructors for fixed-size vectors. Actually the constructor functor should be called vector_size_constructor: it takes a single argument and returns a new vector of the given size.

Using those constructors with an argument different than the fixed size triggers a failed assertion:

Deployer [S]> eigen_vector2(2)
 =                     6.90771e-310        
1.54278e-316        

Deployer [S]> eigen_vector2(3)
deployer-gnulinux: /usr/include/eigen3/Eigen/src/Core/PlainObjectBase.h:285: void Eigen::PlainObjectBase<Derived>::resize(Eigen::Index) [with Derived = Eigen::Matrix<double, 2, 1>; Eigen::Index = long int]: Assertion `((SizeAtCompileTime == Dynamic && (MaxSizeAtCompileTime==Dynamic || size<=MaxSizeAtCompileTime)) || SizeAtCompileTime == size) && size>=0' failed.
Aborted

eigen_typekit/eigen_typekit.cpp Outdated Show resolved Hide resolved
@ahoarau
Copy link
Contributor Author

ahoarau commented Jul 3, 2019

I addressed the issues, and added a fixed size vector value init : eigen_vector2(123) will setConstant to 123.

import("eigen_typekit")

var eigen_vector2 a
var eigen_vector2 b = array(1.,2.)

// Testing to copy from a bigger vector
var eigen_vector2 b2 = array(1.,2.,3.,4.,5.,6.)
// --> b2 is still copying the first 2 elements !!

var eigen_vector3 c
var eigen_vector3 d = array(1.,2.,3.)

var eigen_vector4 e
var eigen_vector4 f = array(1.,2.,3.,4.)

What's bothering me is that var eigen_vector2 b2 = array(1.,2.,3.) will output :

Deployer [S]> var eigen_vector2 b2 = array(1.,2.,3.)
3.809 [ ERROR  ][Logger] Cannot copy an std vector of size 3 into a fixed size vector of size 2
 =                     1                   
2                   

The array is copied anyway probably because it is trying to assing each value individualy with the "[]" operator (theory).

Copy link
Member

@meyerj meyerj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing my comments and sorry for the long delay since you opened this PR.

I suggest to return a well-defined fixed-size vector (e.g. all zeros) when constructing it from an array of the wrong size (see comment below).

ptr( new VectorType ){}
const VectorType& operator()(int size,double value ) const
{
ptr->resize(size);
ptr->conservativeResizeLike(VectorType::Zero(size));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line can be removed? The assignment below will implicitly resize the Eigen vector.

if(ptr->size() != values.size() && VectorType::RowsAtCompileTime == Eigen::Dynamic)
{
ptr->conservativeResize(values.size());
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the dynamic-sized vector there is no need to resize explicitly. The assignment below will resize the vector as needed and all values will be overwritten anyway.

log(Error) << "Cannot copy an std vector of size " << values.size()
<< " into a fixed size vector of size " << ptr->size()
<< endlog();
return *(ptr);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no way to let an Orocos operator fail. In any case it should return a well-defined value, e.g. VectorType::Constant(RTT::internal::NA<double&>::na()). At the moment the instance ptr owned by the constructor singleton is not overwritten, and therefore the result is either undefined or whatever fixed-size vector has been constructed before.

See also #34.

@@ -138,7 +138,7 @@ namespace Eigen{
if (arg->isAssignable()) {
typedef typename RTT::internal::AssignableDataSource<VectorType >::shared_ptr sh_ptr;
sh_ptr asarg = RTT::internal::AssignableDataSource<VectorType >::narrow( arg.get() );
asarg->set().resize( size );
asarg->set().conservativeResizeLike(VectorType::Zero(size));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@@ -375,7 +418,7 @@ namespace Eigen{
ptr( new VectorType() ){}
const VectorType& operator()(int size ) const
{
ptr->resize(size);
ptr->conservativeResizeLike(VectorType::Zero(size));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*ptr = VectorType::Zero(size) ?

No need to keep the previous values in ptr. Over its lifetime it will only be resized and copied, so all elements are already zero.

@@ -402,11 +445,11 @@ namespace Eigen{
const MatrixType& operator()(int size1,int size2) const
{
ptr->resize(size1,size2);
ptr->setZero();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simpler:
*ptr = MatrixType::Zero(size1,size2);

@ahoarau
Copy link
Contributor Author

ahoarau commented Jul 5, 2019

I took care of the comments. Now I'm wondering how to set to zero all the elements of the fixed-size vectors.
Could'nt manage to pass a function to RTT::types::Types()->type("eigen_vector2")->addConstructor(types::newConstructor(vector_fixed_value_constructor<Vector2d>()));.
Or create an unary function with a void argument.

Copy link
Member

@meyerj meyerj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the patch and for addressing review comments, even after more than one and a half years!

@meyerj meyerj merged commit f3aea9d into orocos:toolchain-2.9 Jul 23, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants