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

Implement externally owned vector and matrices #430

Closed
traversaro opened this issue Mar 30, 2018 · 32 comments
Closed

Implement externally owned vector and matrices #430

traversaro opened this issue Mar 30, 2018 · 32 comments
Assignees

Comments

@traversaro
Copy link
Member

To support reading and writing from and to Vector and Matrices already allocated in a form different from iDynTree::VectorDynSize, iDynTree::MatrixDynSize, iDynTree::VectorFixSize, iDynTree::MatrixFixSize (such as Eigen::MatrixXd, std::vector<double> , std::array<double>, raw c-buffers, numpy matrices, etc etc, it may be useful to define classes to represent Vector and Matrices externally owned.

While the matrix implementation is probably more complicated, the Vector implementation could be probably as simple as:

namespace iDynTree
{
class VectorExt
{
private:
    double * m_data;
    size_t m_size;
public:
    VectorExt(double * data, size_t size); 
}
}

The most used methods of the classes such as KinDynComputations and InverseKinematics can be modified to accept this VectorExt class, to simplify interoperability and reducing the need of unnecessary copy, while avoiding dealing directly with raw pointers and preserving the possibility of checking sizes.

A thing that needs to be check is how to properly differentiate external buffers that can be modified to external buffers that can be only read.

@traversaro
Copy link
Member Author

traversaro commented Mar 30, 2018

@traversaro
Copy link
Member Author

traversaro commented Mar 30, 2018

Related discussion at C++ standard committee :

@S-Dafarra
Copy link
Contributor

S-Dafarra commented Apr 9, 2018

Regarding the vector, it seems to me that VectorDynSize has already the structure of VectorExt. Do you think it may be a possibility to implement "maps" as methods of VectorDynSize?

In this case

A thing that needs to be check is how to properly differentiate external buffers that can be modified to external buffers that can be only read.

would still be an open issue. A possibility could be to use a private flag to discern whether the vector can be resized or not (using asserts similarly to what Eigen does for the maps).

On the other hand, we would not need to change any interface.

@traversaro
Copy link
Member Author

Regarding the vector, it seems to me that VectorDynSize has already the structure of VectorExt. Do you think it may be a possibility to implement "maps" as methods of VectorDynSize?

VectorDynSize supports the resize method, and so it cannot be adapted to be a "VectorExt" without changing drastically its meaning and probably breaking a lot of code that use it.

A possibility could be to use a private flag to discern whether the vector can be resized or not (using asserts similarly to what Eigen does for the maps).

I check the proposal, and the std::span proposal is quite reasonable and address also these points , and will be probably accepted in C++20. A possible strategy is to just copy a liberally licensed existing implementation of span or implement and class with the same interface.

On the other hand, mdspan (the equivalent of span for n-order tensors, for n greater than one) is quite complicated and it is not clear if it will be eventually integrated in the standard, so I am afraid that rolling our solution for it may be the best option.

@S-Dafarra
Copy link
Contributor

@traversaro
Copy link
Member Author

traversaro commented Apr 9, 2018

The abseil one is Apache 2-licensed, so it is not directly incorporable in the iDynTree codebase (see https://www.dwheeler.com/essays/floss-license-slide.html). Furthermore, I have the impression that extracting the span implementation from the complete class would be quite complicated.

The gsl-lite one is MIT-licensed, so we can incorporate it directly in the iDynTree. I think we can make iDynTree C++14 only at this point (we discussed about this at a YARP level, so it should not be a problem). The gsl::span seems quite indipendent, cleaning up the gsl specific stuff (gsl_api-> , gsl_constexpr14 --> constexpr, etc etc) I think it could be include in iDynTree as iDynTree::Span (I think we can copy the capitalization from abseil to minimize the risk of collisions in code that use both using namespace iDynTree and using namespace std.

@S-Dafarra
Copy link
Contributor

I think it could be include in iDynTree as iDynTree::Span (I think we can copy the capitalization from abseil to minimize the risk of collisions in code that use both using namespace iDynTree and using namespace std

👍

The gsl::span seems quite indipendent, cleaning up the gsl specific stuff (gsl_api-> , gsl_constexpr14 --> constexpr, etc etc)

Were you thinking about a rough copy/paste or wrapping it as iDynTree::Span?

@traversaro
Copy link
Member Author

Copy/paste from gsl + cleanup of gsl stuff + renaming as iDynTree::Span.
After that, we can start using it in the interfaces as iDynTree::Span.

@traversaro
Copy link
Member Author

Microsoft's GSL also has a MIT-license implementation of span : https://github.com/Microsoft/GSL/blob/master/include/gsl/span .

@traversaro
Copy link
Member Author

cc @diegoferigo I suspect this could be useful for wb-toolbox .

@S-Dafarra
Copy link
Contributor

S-Dafarra commented Apr 9, 2018

Microsoft's GSL also has a MIT-license implementation of span :

I was looking at this table and I did not realize what M stood for 😅

It seems to be much simpler!

@traversaro
Copy link
Member Author

Yes, because it is already C++14.

I think we can drop the methods returning using the std::byte, substitute Expects with std::assert and do a general cleanup and probably that one is already read to be a good iDynTree::Span .

@S-Dafarra
Copy link
Contributor

Regarding narrow_cast see microsoft/GSL#167

@traversaro
Copy link
Member Author

I think we can also drop a few constexpr if that is a problem with Visual Studio 2015.

@S-Dafarra
Copy link
Contributor

narrow_cast can be changed easily into static_cast (see https://github.com/Microsoft/GSL/blob/master/include/gsl/gsl_util#L92), while narrow is a bit more tricky since it checks that the value has not changed after the cast (see https://github.com/Microsoft/GSL/blob/master/include/gsl/gsl_util#L110). Does it make sense to keep this check?

@traversaro
Copy link
Member Author

I think we can just switch to static_cast.

@traversaro
Copy link
Member Author

But the actual narrow call is quite small, so we can copy also that if you feel strongly about it.

@S-Dafarra
Copy link
Contributor

Considering that in iDynTree we have only vector and matrices as containers, I wonder whether it is better to avoid considering a generic multi-span class while rather using something that eases the passage from iDynTree to Eigen and eventuallymatio 🤔 . In this sense, we could use something similar to span with some additional field to discern, for example, whether it is row or column major.

@traversaro
Copy link
Member Author

I totally agree, especially because the ndspan proposal seems to be quite complicated and its inclusion in the standard is uncertain.

@S-Dafarra
Copy link
Contributor

I would call this new class something like matrix_view or similar, following the string_view style. Regarding the implementation, I don't know whether there is some implementation we could already use. If we are going to implement it from scratch, I think I will not be able to have the same performances of span 😅

@traversaro
Copy link
Member Author

I think I will not be able to have the same performances of span

I don't know, I don't think we can get too wrong passing a pointer and two integers around. : )
For the _view name I don't know, I read somewhere in all the standard proposal that _view was associated with the concept of read-only reference, while _ref was associated with the concept of a reference that could also have write support. Indeed, the original name for ndspan was arrray_ref .

@traversaro
Copy link
Member Author

Map is also quite an interesting suffix, given that it is used in Eigen so a lot of developers would be familiar with the concept.

@S-Dafarra
Copy link
Contributor

I was referring to all the machinery that makes span and make_span working effectively with a "general" vector.

Regarding the name I like the suffix Map. What about MatrixMap?

@traversaro
Copy link
Member Author

traversaro commented Apr 10, 2018

MatrixMap

👍
Assuming that we agree that we don't think to handle the stride stuff at this point, I think the trickiest part is deciding if the row or column major information is a member that can change at runtime, or a template parameter specified at compile time. The compile-time route is attractive for compatibility with Eigen, but it may be problematic for declaring non-header only methods that can take in input both variants.

@S-Dafarra
Copy link
Contributor

S-Dafarra commented Apr 11, 2018

Assuming that we agree that we don't think to handle the stride stuff at this point

👍

The compile-time route is attractive for compatibility with Eigen, but it may be problematic for declaring non-header only methods that can take in input both variants.

Sincerely I prefer the compile-time option, also because I don't know if it makes sense for this information to change at run-time. Looking at the span code I saw line https://github.com/Microsoft/GSL/blob/master/include/gsl/span#L333. If I have undestood correctly (and I have big doubts about that) the public member value_type gives you information about the type of value stored, even if this information comes from the template. I was thinking of doing the same, i.e. accept the type RowMajor or ColumnMajor from template, while exposing a public member referring to it. Do you think this may solve the issue you pointed out?

Maybe we can add for our current definition of MatrixStorageOrdering a similar implementation of std::is_same in order to check which ordering is actually used, maybe using something like:

constexpr bool isRowMajor() const noexcept { return iDynTree::is_same<iDynTree::RowMajor, order_type>::value; }

where order_type is the public member I was mentioning before.

@prashanthr05
Copy link
Contributor

prashanthr05 commented Jan 3, 2019

An example for iDynTree::Span in action for the use-case of getting and setting states,

bool getState(iDynTree::Span<double>& x) const
{
    x(0) = 0.0;
    return true;
}

bool setState(iDynTree::Span<double>& x)
{
    // ... check if x and internal state have same size, else throw error
    // avoiding all that here
   double state = x(0);
   return true;
}

can be called using,

iDynTree::VectorDynSize state;
state.resize(1);
state(0) = 10.0;
std::cout << state.toString() << std::endl; // prints 10.0
iDynTree::Span<double> buf(state.data(), state.size());
getState(buf);
std::cout << state.toString() << std::endl; // prints 0.0 

state(0) = 10.0;
iDynTree::Span<double> setBuf(state.data(), state.size());
setState(setBuf);

@traversaro
Copy link
Member Author

traversaro commented Jan 3, 2019

Exactly! I would however add asSpan methods to VectorDynSize and VectorFixSize, so you could just do:

setState(state.asSpan())

@S-Dafarra
Copy link
Contributor

Actually the make_span method works already for iDynTree vectors. You could do

setState(iDynTree::make_span(state))

@traversaro
Copy link
Member Author

You are right, I totally forgot about it!

@traversaro
Copy link
Member Author

A self-declared "production-ready" implementation of mdspan is available at https://github.com/kokkos/mdspan .

@GiulioRomualdi
Copy link
Member

I think we can close this issue right?

@traversaro

@traversaro
Copy link
Member Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants