Linear Algebra In C++ With XTensor Is Just Like Numpy | by Emmett Boudreau | Towards Data Science

Notebook
A concept that is very important to scientific computing and Data Science is the ability to manipulate, create, and utilize matrices as types within a given programming language. For Python, Data-Scientists use the Numpy library, which has a fantastic and easy to understand approach to creating and manipulating matrices inside of Python. For Julia, the idea of matrices and vectors was included from the inception of the language. For C++, there is a fantastic new up-and-coming library called XTensor.
XTensor is a C++ library that allows a programmer to easily utilize matrices and arrays with similar syntax to that of Numpy. The similarity to Numpy is XTensor’s real strength in that the transition between the two can be done with relative ease. In order to use XTensor, you will of course need to install it. Fortunately for Data-Scientists, this can easily be done through the Conda package manager in your environment with
conda install -c conda-forge xtensor
Alternatively, if you are on a Debian or Debian-Based Linux distribution, XTensor is available through the Apt package manager:
sudo apt-get install xtensor-dev
Last but not least, you could always build from source if you’re on FreeBSD or another Unix-like operating system and have the proper essential build headers:
cmake -DCMAKE_INSTALL_PREFIX=path_to_prefix ..make install
Alternatively for Windows users:
mkdir buildcd buildcmake -G "NMake Makefiles" -DCMAKE_INSTALL_PREFIX=path_to_prefix ..nmakenmake install
If you are a Python and Numpy user, then you are in luck with XTensor. Here is a link to an absolutely invaluable Numpy to XTensor cheat-sheet which you can use to apply your Numpy familiarity:
xtensor.readthedocs.io
This is going to make the transition from Numpy to XTensor incredibly simple and stream-lined for Python-based Data Scientists and end-users. After installing XTensor, you can compile and execute the following code to ensure that the headers are now on your machine:
#include <iostream>// Base Arrays:#include "xtensor/xarray.hpp"// XTensor IO:#include "xtensor/xio.hpp"// XTensor View:#include "xtensor/xview.hpp"int main(){return 0;}
If nothing happens whenever you compile and run that code, then
congratulations!
Your installation was successful and XTensor is now available on your machine. If the output was an error, you will most likely need to try your luck with a different installation method, or build from source. Another important thing to ensure is that you are in the Conda environment that you installed XTensor in, of course.
Moving on, we will of course start our new code by adding the same inclusions we did in that little test.
#include <iostream>// Base Arrays:#include "xtensor/xarray.hpp"// XTensor IO:#include "xtensor/xio.hpp"// XTensor View:#include "xtensor/xview.hpp"
Including these headers will give us a new name-space, xt. For starters, in order to create a matrix, we are going to use the xarray object template from the xt name-space. For this example, we will have a matrix full of integers, so we will provide the “ int” argument whenever we create our new object. I am just going to name it array, and we will put the some integers into it.
xt::xarray<int>array({{500, 500, 300}, {500, 600, 800}})
There are two different types of XTensor matrices:
xarray — Matrices with a dynamic dimension set.
xtensor — Matrices with a static dimension set.
Next, if we are in a notebook, then we can simply type “ array” to show our new matrix.
Alternatively, if you are compiling and executing code, then you will need to use cout and put this code into a main() function of course. I am not going to be doing that in this example, as I am working in a notebook, but here is how you would structure this code, for example:
#include <iostream>// Base Arrays:#include "xtensor/xarray.hpp"// XTensor IO:#include "xtensor/xio.hpp"// XTensor View:#include "xtensor/xview.hpp"int main(){    xt::xarray<int>array({{500, 500, 300}, {500, 600, 800}});    std::cout << array;}
Within some IDEs, you might be able to just cout it, as well:
std::cout << array;
This is where including iostream off the bat will come in handy, even if you are working in a notebook.
We can reshape our new array using the reshape() function, which is a child of our array object.
array.reshape({3, 2})
We can also cast a new type into all of the dims of our array. This is useful, for example if we wanted the ability to count fractional parts but originally created an matrix full of integers. We can do this using the cast function, which is back in the xt namespace.
xt::cast<double>(array)
We also have access to all of the typical Numpy initializers/generators that we can use to create new matrices. A very useful version of this commonly used in Machine-learning is np.zeroes(). The equivalent to this in XTensor is zeros from the xt name-space, which is of course an object that inherits from our regular xarray. Note that it does inherit from xarray and not xtensor, so the dimensions of our new found matrix will be mutable.
xt::zeros<double>({3, 4})
This also applies to the other generators available in Numpy that you might expect to carry over, such as eye, ones, and many others.
We can of course also index our arrays in the same way we would with Numpy. One minor difference is that parenthesis are used to call indexes, rather than brackets.
While having access to generators and the creation of matrices is nice, XTensor is of course a lot more than that to be considered Numpy-equivalent. We can basically do any of the operations available to us in Numpy, such as concatenating two matrices together:
// Our second arrayxt::xarray<int>arraytwo({{500, 500, 300}, {500, 600, 800}})// Reshape it to fit first array's dimensionsarraytwo.reshape({3, 2})// Concatenate it to the first arrayxt::concatenate(xtuple(array, arraytwo))
We could also squeeze, for example:
xt::squeeze(array)
We also have access to the standard operations that you might need to perform with a matrix, for example summations:
xt::sum(array, {0, 1})//      ^^ Array  ^^^ Axis
Mean:
xt::mean(array)
And STD:
xt::stddev(array, {0,1})
Furthermore, we have access to linear algebraic operations such as dot, for example. To access these, you will need to access the linalg name-space, which is stored within the xt name-space, like so:
xt::linalg::dot(array, arraytwo)
In order to get this name-space, you might need to go grab XTensor-blas, as well.
github.com
XTensor is a really great tool that brings the ease of use and standardization of Numpy for scientific calculation and work with matrices to the forefront of C++. C++ is a great language to use in general, because of its venerability — being decades old at this point and well-used in its lifetime.
Furthermore, I think that XTensor creates a great outlet for Data Scientists with interest in using C++ in Data Science and statistical applications. If nothing else, XTensor is quite simply a really cool library that could potentially bridge the gap between a Python user and a C++ user.