xtensor-stack / xtensor

C++ tensors with broadcasting and lazy computing
BSD 3-Clause "New" or "Revised" License
3.33k stars 398 forks source link

Transposing a view throws an expection #651

Closed schra closed 6 years ago

schra commented 6 years ago

This code

xt::xarray<int> vector = xt::linspace(1, 10, 10);
auto matrix = xt::view(vector, xt::all(), xt::newaxis());

std::cout << xt::transpose(matrix) << std::endl;

throws an expection:

terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
Aborted (core dumped)

The expected behavior is to print the following:

{{ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10}}

I'm using the master branch of xtensor (0872366685eafb4b251a355e3340986256212553).

Just to be clear: The intend of my C++ code is to express this NumPy code:

linspace(1, 10, 10)[:, newaxis].T

Output of GDB:

(gdb) bt
#0  __cxxabiv1::__cxa_throw (obj=0x55555578a4e0, tinfo=0x7ffff7279dd0 <typeinfo for std::bad_alloc>, dest=0x7ffff6f8bb60 <std::bad_alloc::~bad_alloc()>) at /build/gcc/src/gcc/libstdc++-v3/libsupc++/eh_throw.cc:75
#1  0x00007ffff6fb8104 in std::__throw_bad_alloc () at /build/gcc/src/gcc/libstdc++-v3/src/c++11/functexcept.cc:54
#2  0x0000555555558b77 in __gnu_cxx::new_allocator<unsigned long>::allocate (this=0x7fffffffe2b0, __n=18446744073709551545) at /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/7.2.1/../../../../include/c++/7.2.1/ext/new_allocator.h:102
#3  0x0000555555558a23 in xt::svector<unsigned long, 4ul, std::allocator<unsigned long>, true>::grow (this=0x7fffffffe2b0, min_capacity=18446744073709551545) at xtensor/include/xtensor/xstorage.hpp:1102
#4  0x0000555555559bcd in xt::svector<unsigned long, 4ul, std::allocator<unsigned long>, true>::resize (this=0x7fffffffe2b0, n=18446744073709551545) at xtensor/include/xtensor/xstorage.hpp:814
#5  0x000055555555a8f7 in xt::svector<unsigned long, 4ul, std::allocator<unsigned long>, true>::swap<4ul, std::allocator<unsigned long>, true> (this=0x7fffffffe2b0, rhs=...) at xtensor/include/xtensor/xstorage.hpp:1057
#6  0x000055555555a6a1 in xt::svector<unsigned long, 4ul, std::allocator<unsigned long>, true>::svector (this=0x7fffffffe2b0, rhs=...) at xtensor/include/xtensor/xstorage.hpp:751
#7  0x000055555555990f in xt::xstrided_view<xt::xview<xt::xarray_container<xt::uvector<int, std::allocator<int> >, (xt::layout_type)1, xt::svector<unsigned long, 4ul, std::allocator<unsigned long>, true>, xt::xtensor_expression_tag>&, xt::xall<unsigned long>, xt::xnewaxis<unsigned long> >&, xt::svector<unsigned long, 4ul, std::allocator<unsigned long>, true>, xt::uvector<int, std::allocator<int> > const&>::xstrided_view (this=0x7fffffffe2a0, e=..., shape=..., 
    strides=..., offset=0, layout=xt::layout_type::dynamic) at xtensor/include/xtensor/xstrided_view.hpp:253
#8  0x00005555555572ae in xt::transpose<xt::xview<xt::xarray_container<xt::uvector<int, std::allocator<int> >, (xt::layout_type)1, xt::svector<unsigned long, 4ul, std::allocator<unsigned long>, true>, xt::xtensor_expression_tag>&, xt::xall<unsigned long>, xt::xnewaxis<unsigned long> >&> (e=...) at xtensor/include/xtensor/xstrided_view.hpp:762
#9  0x0000555555556b74 in main () at test.cpp:50
wolfv commented 6 years ago

Ok, for some reason we try to allocate a shape with extremely large size in this line #2 (__n=18446744073709551545)...

Looks like subtracting something from a std::size_t. I'll look into it.

SylvainCorlay commented 6 years ago

On cling, everything seems to be ok until std::cout << xt::transpose(matrix) << std::endl;, assigning xt::transpose(matrix) to an xarray equally fails.

wolfv commented 6 years ago

I just tested move assign and swap of svector and it seems to work. Will step through it with GDB.

wolfv commented 6 years ago

Ok, the problem is definitly in this line, investigating:

std::copy(e.shape().rbegin(), e.shape().rend(), shape.begin());

wolfv commented 6 years ago

Nope, wrong, I think now that the problem is that we're not initializing a stride of 0 for xt::newaxis.

wolfv commented 6 years ago

Ah, well ... I got it now. the problem is that we're returning a computed temporary strides container from xt::view. And since we're calling expr.strides().begin(), and expr.strides().end() on two different temporaries, the iterator bounds are invalid.

We could take strides as a copy, but that doesn't sound like a great solution. Or we could somehow cache the strides in the xview which sounds like a slightly better solution. Thoughts? @SylvainCorlay @JohanMabille

JohanMabille commented 6 years ago

@wolfv I would go for caching the strides in the view, and computes them the first time we need them (as we do for the shape of xfunction_base.