From numpy to xtensor



Two container types are provided. xarray (dynamic number of dimensions) and xtensor (static number of dimensions).

Python 3 - numpy C++ 14 - xtensor
np.array([[3, 4], [5, 6]])
xt::xarray<double>({{3, 4}, {5, 6}})
xt::xtensor<double, 2>({{3, 4}, {5, 6}})
arr.reshape([3, 4]) arr.reshape{{3, 4})


Lazy helper functions return tensor expressions. Return types don’t hold any value and are evaluated upon access or assignment. They can be assigned to a container or directly used in expressions.

Python 3 - numpy C++ 14 - xtensor
np.linspace(1.0, 10.0, 100) xt::linspace<double>(1.0, 10.0, 100)
np.logspace(2.0, 3.0, 4) xt::logspace<double>(2.0, 3.0, 4)
np.arange(3, 7) xt::arange(3, 7)
np.eye(4) xt::eye(4)
np.zeros([3, 4]) xt::zeros<double>({3, 4})
np.ones([3, 4]) xt::ones<double>({3, 4})
np.meshgrid(x0, x1, x2, indexing='ij') xt::meshgrid(x0, x1, x2)

xtensor’s meshgrid implementation corresponds to numpy’s 'ij' indexing order.


xtensor offers lazy numpy-style broadcasting, and universal functions. Unlike numpy, no copy or temporary variables are created.

Python 3 - numpy C++ 14 - xtensor
a[:, np.newaxis]
a[:5, 1:]
a[5:1:-1, :]
xt::view(a, xt::all(), xt::newaxis())
xt::view(a, xt::range(_, 5), xt::range(1, _))
xt::view(a, xt::range(5, 1, -1), xt::all())
np.broadcast(a, [4, 5, 7]) xt::broadcast(a, {4, 5, 7})
np.vectorize(f) xt::vectorize(f)
a[a > 5] xt::filter(a, a > 5)
a[[0, 1], [0, 0]] xt::index_view(a, {{0, 0}, {1, 0}})


The random module provides simple ways to create random tensor expressions, lazily.

Python 3 - numpy C++ 14 - xtensor
np.random.seed(0) xt::random::seed(0)
np.random.randn(10, 10) xt::random::randn<double>({10, 10})
np.random.randint(10, 10) xt::random::randint<int>({10, 10})
np.random.rand(3, 4) xt::random::rand<double>({3, 4})


Concatenating expressions does not allocate memory, it returns a tensor expression holding closures on the specified arguments.

Python 3 - numpy C++ 14 - xtensor
np.stack([a, b, c], axis=1) xt::stack(xtuple(a, b, c), 1)
np.concatenate([a, b, c], axis=1) xt::concatenate(xtuple(a, b, c), 1)

Diagonal, triangular and flip

In the same spirit as concatenation, the following operations do not allocate any memory and do not modify the underlying xexpression.

Python 3 - numpy C++ 14 - xtensor
np.diag(a) xt::diag(a)
np.diagonal(a) xt::diagonal(a)
np.triu(a) xt::triu(a)
np.tril(a, k=1) xt::tril(a, 1)
np.flip(a, axis=3) xt::flip(a, 3)
np.flipud(a) xt::flip(a, 0)
np.fliplr(a) xt::flip(a, 1)


xtensor follows the idioms of the C++ STL providing iterator pairs to iterate on arrays in different fashions.

Python 3 - numpy C++ 14 - xtensor
for x in np.nditer(a):
for(auto it=a.xbegin(); it!=a.xend(); ++it)
Iterating with a prescribed broadcasting shape
for(auto it=a.xbegin({3, 4});
it!=a.xend({3, 4}); ++it)


Logical universal functions are truly lazy. xt::where(condition, a, b) does not evaluate a where condition is falsy, and it does not evaluate b where condition is truthy.

Python 3 - numpy C++ 14 - xtensor
np.where(a > 5, a, b) xt::where(a > 5, a, b)
np.where(a > 5) xt::where(a > 5)
np.any(a) xt::any(a)
np.all(a) xt::all(a)
np.logical_and(a, b) a && b
np.logical_or(a, b) a || b


Python 3 - numpy C++ 14 - xtensor
np.equal(a, b) xt::equal(a, b)
np.not_equal(a) xt::not_equal(a)
np.nonzero(a) xt::nonzero(a)

Complex numbers

Functions xt::real and xt::imag respectively return views on the real and imaginary part of a complex expression. The returned value is an expression holding a closure on the passed argument.

Python 3 - numpy C++ 14 - xtensor
np.real(a) xt::real(a)
np.imag(a) xt::imag(a)
  • The constness and value category (rvalue / lvalue) of real(a) is the same as that of a. Hence, if a is a non-const lvalue, real(a) is an non-const lvalue reference, to which one can assign a real expression.
  • If a has complex values, the same holds for imag(a). The constness and value category of imag(a) is the same as that of a.
  • If a has real values, imag(a) returns zeros(a.shape()).


Reducers accumulate values of tensor expressions along specified axes. When no axis is specified, values are accumulated along all axes. Reducers are lazy, meaning that returned expressons don’t hold any values and are computed upon access or assigmnent.

Python 3 - numpy C++ 14 - xtensor
np.sum(a, axis=[0, 1]) xt::sum(a, {0, 1})
np.sum(a) xt::sum(a), axis=1) xt::prod(a, {1}) xt::prod(a)
np.mean(a, axis=1) xt::mean(a, {1})
np.mean(a) xt::mean(a)

More generally, one can use the xt::reduce(function, input, axes) which allows the specification of an arbitrary binary function for the reduction. The binary function must be cummutative and associative up to rounding errors.

Mathematical functions

xtensor universal functions are provided for a large set number of mathematical functions.

Basic functions:

Python 3 - numpy C++ 14 - xtensor
np.isnan(a) xt::isnan(a)
np.absolute(a) xt::abs(a)
np.sign(a) xt::sign(a)
np.remainder(a, b) xt::remainder(a, b)
np.clip(a, min, max) xt::clip(a, min, max)
  xt::fma(a, b, c)

Exponential functions:

Python 3 - numpy C++ 14 - xtensor
np.exp(a) xt::exp(a)
np.expm1(a) xt::expm1(a)
np.log(a) xt::log(a)
np.log1p(a) xt::log1p(a)

Power functions:

Python 3 - numpy C++ 14 - xtensor
np.power(a, p) xt::pow(a, b)
np.sqrt(a) xt::sqrt(a)
np.cbrt(a) xt::cbrt(a)

Trigonometric functions:

Python 3 - numpy C++ 14 - xtensor
np.sin(a) xt::sin(a)
np.cos(a) xt::cos(a)
np.tan(a) xt::tan(a)

Hyperbolic functions:

Python 3 - numpy C++ 14 - xtensor
np.sinh(a) xt::sinh(a)
np.cosh(a) xt::cosh(a)
np.tang(a) xt::tanh(a)

Error and gamma functions:

Python 3 - numpy C++ 14 - xtensor
scipy.special.erf(a) xt::erf(a)
scipy.special.gamma(a) xt::tgamma(a)
scipy.special.gammaln(a) xt::lgamma(a)