This article is the second one of the Read Xtensor Source Code searies. In the previous article, I build and install xtensor and xtl on my windows system desktop, and in this article, I'll try to explore the xtensor document, write some examples and try to understand the phylosophy and idioms behind the xtensor design.
After using xtensor a while, it leave me a very complex impression. On one hand it's well designed and try hard to use C++11 features and C++11 idioms, which ensure the code base quality; it uses very seemingly syntax to Python which make it very easy use. On the other hand, it has implicit bugs, I'll talk about them in this article.
This article will keep growing, all experimenting examples will be dumped here.
1. getting started
Pythoner can easily guess what the following code will output.
void getting_started()
{
xt::xarray<double> arr1{
{ 1.0, 2.0, 3.0 },
{ 2.0, 5.0, 7.0 },
{ 2.0, 5.0, 7.0 }
};
xt::xarray<double> arr2{
5.0, 6.0, 7.0
};
xt::xarray<double> res = xt::view(arr1, 1) + arr2;
std::cout << res << std::endl;
std::cout << arr1(1, 1) << std::endl;
std::cout << arr2(1) << std::endl;
// do pow of arr2 for arr1
arr2.reshape({3, 1});
res = xt::pow(arr1, arr2);
std::cout << res << std::endl;
arr1.reshape({1, 9});
std::cout << arr1;
}
2. lazy evaluation
Xtensor is featured for lazy evaluation, which make it very efficient. It utilize a very complexed expression and semantic machism. I wrote the following example, but can't see explicitly how much it can speed up.
void expression()
{
xt::xarray<double> x{1.0, 2.0, 3.0, 4.0};
xt::xarray<double> y{5.0, 6.0, 7.0, 8.0};
xt::xarray<double> z{0, 3.14, 6.28, 9.42};
xt::xarray<double> res = x + y + xt::sin(z);
std::cout << res << std::endl;
}
void expression_interface()
{
using array_type = xt::xarray<double>;
using shape_type = array_type::shape_type;
shape_type shape = {3, 2, 4};
array_type arr{shape};
size_t d = arr.dimension();
std::cout << "dimension: " << d << std::endl;
const shape_type& s = arr.shape();
bool res = (d == shape.size()) && (s == shape);
std::cout << res << std::endl;
}
3. sliced view
The slice machism is exactly same to Python, but it won't check the range of the range, in the following example, I wrong a very large upper bound, the output will just siliently trunck the container, no even a warning message is emited out.
Another thing bothored me is that one can assign a scalar to a container, which will resize the container to a 0-D container, it's very error prone. For example, in the following code, I want to assign sequence numbers to array, it should be item = i++
, but I mistyped it as a = i++
, it just work, but of course not what I want.
void view_sliced()
{
std::vector<size_t> shape{ 3, 2, 4 };
xt::xarray<int> a{ shape };
int i = 0;
for (auto &item: a) {
item = i++; // a is assigned to a scalar, then a is resized to a scalar?!
}
std::cout << a << std::endl;
std::cout << "----------" << std::endl;
auto v1 = xt::view(a, xt::range(1, 3)); // shape: 2, 2, 4
std::cout << v1.dimension() << std::endl;
// auto v1 = xt::view(a, xt::range(1, 3), xt::all(), xt::range(1, 3));
std::cout << v1 << std::endl;
std::cout << "----------" << std::endl;
auto v2 = xt::view(a, xt::all(), xt::range(1, 100), xt::all()); // bug or feature? range error
std::cout << v2 << std::endl;
std::cout << "----------" << std::endl;
auto v3 = xt::view(a, xt::all(), xt::all(), xt::range(1, 100)); // the upper bound don't work
std::cout << v3 << std::endl;
}