xtensor icon indicating copy to clipboard operation
xtensor copied to clipboard

Custom datatype usage

Open samuel-emrys opened this issue 4 years ago • 4 comments

Hi all, not sure if this is the best place for discussion of this, but I couldn't find another way to get in contact/discuss. I haven't started using the library yet, but I'm interested in the features it offers. In fact - it looks to be exactly what I'm looking for. However, going through the documentation, I haven't been able to find any detailed discussion of data type requirements for some of the operations. I have some accuracy and precision requirements related the storage of financial information - specifically that floating point representations aren't able to sufficiently represent monetary values. With that in mind, I'm looking at utilising something like the Compositional Numeric Library or boost::multiprecision::cpp_dec_float, which provide fixed point and fixed precision data types respectively.

What I'm not clear about is what requirements need to be satisfied in order for the feature set of xtensor to be valid. i.e., what are the attributes of a data type that would enable it to be used as something like:

using fpe = cnl::fixed_point<cnl::elastic_integer<31>, -31>
xt::xarray<fpe> arr1{{fpe{0.345}, fpe{0.654}}, {fpe{0.5000001}, fpe{0.43600002}}}
auto b = xt::prod(arr1)();

Does the data type need to implement an operator*() overload here? Does it need to return true for std::is_arithmetic<T>? What other requirements are there? Or are only primitives supported? Any more information would be appreciated.

samuel-emrys avatar Apr 30 '21 15:04 samuel-emrys

Hi,

As long as your type defines the usual arithmetic operations, it can be used as the value_type of an xtensor expression. You can override the is_arithmetic and other traits that are redifined in the xtl namespace (see https://github.com/xtensor-stack/xtl/blob/master/include/xtl/xtype_traits.hpp#L31).

The default implementation is to inherit form the traits defined in the STL, so specializing the STL traits should work too.

JohanMabille avatar May 02 '21 20:05 JohanMabille

Thanks for the explanation @JohanMabille. Do you think a description of this and an example should be included in the documentation?

samuel-emrys avatar May 02 '21 22:05 samuel-emrys

Yes that would be really useful. I think the Usage section would be the right place to add it.

JohanMabille avatar May 03 '21 08:05 JohanMabille

Hi @JohanMabille, I'm attempting to build a custom data type, but I'm running in to problems. I can get a trivial wrapper for a number to work, but as soon as I introduce any limitations on the operations between types I'm starting to run in to issues. To be explicit, here's a minimum working example of the problem I'm running in to:

https://godbolt.org/z/YePqdEMn6

Essentially, without the boilerplate, I have a Money struct that looks like the following:

using decimal = boost::multiprecision::cpp_dec_float_50;
struct Money {
    decimal mAmount;
    std::string mCurrency;
};

The salient logic that's been implemented here is that arithmetic operations between Money objects that don't share the same mCurrency value should be prohibited. A default initialised Money object is created with currency "XXX" (in this case I'm using default initialised to mean int initialised - I've discovered that the class actually needs an int castable constructor to be instantiated within an xarray). I'm trying to initialise an xarray as follows:

auto MoneyDecimal = Money{123456789.1234567890123_dec, "USD"};

xt::xarray<Money> arr{
    MoneyDecimal, MoneyDecimal
};

And then take the product of these two elements:

auto xt_mult_money = xt::prod(arr)(); // exception

This fails, and the reason it appears to fail is because:

  • It default initialises objects to the xarray rather than utilising the constructor arguments provided to xarray. I can see that this (might) make sense in the context of growing the size of the xarray? Even so, I would expect that there would be no objects actually allocated to the array except what I place in there.
  • At some stage, a multiplication is attempted between a default initialised object and the MoneyDecimal objects I've specified in the constructor. This throws an exception (as I've implemented it this way), and as it should since the currency types don't match.

I've tried to debug this by digging in to the source code, but I'm having a really hard time following it with all the macro definitions. I guess my questions for you are:

  • Why would a multiplication with a default initialized object take place?
  • Why would default-constructed objects even be present after the initialisation and allocation of arr? Shouldn't the object have already been created by the time we apply xt::prod to it?
  • Are you able to suggest any additional considerations for my class to implement to be compliant with the underlying implementation?
  • Are there any other assumptions that I need to be aware of in designing a custom class?

Any help you can provide is appreciated.

samuel-emrys avatar Jun 20 '21 11:06 samuel-emrys