.. Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht

   Distributed under the terms of the BSD 3-Clause License.

   The full license is in the file LICENSE, distributed with this software.

From numpy to xtensor
=====================

.. image:: numpy.svg
   :height: 100px
   :align: right

.. raw:: html

   <style>
   .rst-content table.docutils {
       width: 100%;
       table-layout: fixed;
       border: none;
   }

   table.docutils th {
       text-align: center;
   }

   table.docutils .line-block {
       margin-left: 0;
       margin-bottom: 0;
   }

   table.docutils code.literal {
       color: initial;
   }

   code.docutils {
       background: initial;
       border: none;
   }

   * {
       border: none;
   }

   .rst-content table.docutils thead {
       background-color: #d0e0e0;
   }

   .rst-content table.docutils td {
       border-bottom: none;
       border-left: none;
   }

   .rst-content table.docutils tr:hover {
       background-color: #d0e0e0;
   }

   .rst-content table.docutils:not(.field-list) tr:nth-child(2n-1):hover td {
       background-color: initial;
   }

   #linear-algebra table.docutils thead .row-odd {
       background: #ffdddd;
   }

   #linear-algebra tr:nth-child(2n-1) td {
       background: #f9f3f3;
   }

   #linear-algebra tr:hover {
       background: #ffdddd;
   }

   #linear-algebra tr:nth-child(2n-1):hover td {
       background-color: initial;
   }
   </style>

Containers
----------

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})``                        |
+------------------------------------------------+------------------------------------------------+

Initializers
------------

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.

Broadcasting
------------

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]``                              | | ``xt::view(a, xt::all(), xt::newaxis())``         |
| | ``a[:5, 1:]``                                     | | ``xt::view(a, xt::range(_, 5), xt::range(1, _))`` |
| | ``a[5:1:-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}})``             |
+-----------------------------------------------------+-----------------------------------------------------+

Random
------

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})``          |
+-----------------------------------------------+-----------------------------------------------+

Concatenation
-------------

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)``                            |
+-----------------------------------------------+-----------------------------------------------+

Iteration
---------

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 over ``a`` with a prescribed broadcasting shape      | | ``a.xbegin({3, 4})``                                         |
|                                                                | | ``a.xend({3, 4})``                                           |
+----------------------------------------------------------------+----------------------------------------------------------------+
| Iterating over ``a`` in a row-major fashion                    | | ``a.xbegin<layout_type::row_major>()``                       |
|                                                                | | ``a.xbegin<layout_type::row_major>()``                       |
+----------------------------------------------------------------+----------------------------------------------------------------+
| Iterating over ``a`` in a column-major fashion                 | | ``a.begin<layout_type::column_major>()``                     |
|                                                                | | ``a.xend<layout_type::column_major>()``                      |
+----------------------------------------------------------------+----------------------------------------------------------------+

Logical
-------

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``                                    |
+-----------------------------------------------+-----------------------------------------------+
| ``np.isclose(a, b)``                          | ``xt::isclose(a, b)``                         |
+-----------------------------------------------+-----------------------------------------------+
| ``np.allclose(a, b)``                         | ``xt::allclose(a, b)``                        |
+-----------------------------------------------+-----------------------------------------------+

Comparisons
-----------

+--------------------------------------------+-----------------------------------------------+
|            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
--------

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)``                                |
+-----------------------------------------------+-----------------------------------------------+
| ``np.prod(a, axis=1)``                        | ``xt::prod(a, {1})``                          |
+-----------------------------------------------+-----------------------------------------------+
| ``np.prod(a)``                                | ``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.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)``                             |
+-----------------------------------------------+-----------------------------------------------+

**Classification functions:**

+-----------------------------------------------+-----------------------------------------------+
|            Python 3 - numpy                   |                C++ 14 - xtensor               |
+===============================================+===============================================+
| ``np.isnan(a)``                               | ``xt::isnan(a)``                              |
+-----------------------------------------------+-----------------------------------------------+
| ``np.isinf(a)``                               | ``xt::isinf(a)``                              |
+-----------------------------------------------+-----------------------------------------------+
| ``np.isfinite(a)``                            | ``xt::isfinite(a)``                           |
+-----------------------------------------------+-----------------------------------------------+

Linear algebra
--------------

Many functions found in the ``numpy.linalg`` module are implemented in `xtensor-blas`_, a seperate package offering BLAS and LAPACK bindings, as well as a convenient interface replicating the ``linalg`` module.

Please note, however, that while we're trying to be as close to NumPy as possible, some features are not 
implemented yet. Most prominently that is broadcasting for all functions except for ``dot``.


**Matrix and vector products**

+--------------------------------------+--------------------------------------+
|          Python 3 - numpy            |           C++ 14 - xtensor           |
+======================================+======================================+
| ``np.dot(a, b)``                     | ``xt::linalg::dot(a, b)``            |
+--------------------------------------+--------------------------------------+
| ``np.vdot(a, b)``                    | ``xt::linalg::vdot(a, b)``           |
+--------------------------------------+--------------------------------------+
| ``np.outer(a, b)``                   | ``xt::linalg::outer(a, b)``          |
+--------------------------------------+--------------------------------------+
| ``np.matrix_power(a, 123)``          | ``xt::linalg::matrix_power(a, 123)`` |
+--------------------------------------+--------------------------------------+
| ``np.kron(a, b)``                    | ``xt::linalg::kron(a, b)``           |
+--------------------------------------+--------------------------------------+


**Decompositions**

+-----------------------------+-----------------------------+
|       Python 3 - numpy      |       C++ 14 - xtensor      |
+=============================+=============================+
| ``np.linalg.cholesky(a)``   | ``xt::linalg::cholesky(a)`` |
+-----------------------------+-----------------------------+
| ``np.linalg.qr(a)``         | ``xt::linalg::qr(a)``       |
+-----------------------------+-----------------------------+
| ``np.linalg.svd(a)``        | ``xt::linalg::svd(a)``      |
+-----------------------------+-----------------------------+


**Matrix eigenvalues**

+-----------------------------+-----------------------------+
|       Python 3 - numpy      |       C++ 14 - xtensor      |
+=============================+=============================+
| ``np.linalg.eig(a)``        | ``xt::linalg::eig(a)``      |
+-----------------------------+-----------------------------+
| ``np.linalg.eigvals(a)``    | ``xt::linalg::eigvals(a)``  |
+-----------------------------+-----------------------------+
| ``np.linalg.eigh(a)``       | ``xt::linalg::eigh(a)``     |
+-----------------------------+-----------------------------+
| ``np.linalg.eigvalsh(a)``   | ``xt::linalg::eigvalsh(a)`` |
+-----------------------------+-----------------------------+

**Norms and other numbers**

+--------------------------------+--------------------------------+
|        Python 3 - numpy        |        C++ 14 - xtensor        |
+================================+================================+
| ``np.linalg.norm(a, order=2)`` | ``xt::linalg::norm(a, 2)``     |
+--------------------------------+--------------------------------+
| ``np.linalg.cond(a)``          | ``xt::linalg::cond(a)``        |
+--------------------------------+--------------------------------+
| ``np.linalg.det(a)``           | ``xt::linalg::det(a)``         |
+--------------------------------+--------------------------------+
| ``np.linalg.matrix_rank(a)``   | ``xt::linalg::matrix_rank(a)`` |
+--------------------------------+--------------------------------+
| ``np.linalg.slogdet(a)``       | ``xt::linalg::slogdet(a)``     |
+--------------------------------+--------------------------------+
| ``np.trace(a)``                | ``xt::linalg::trace(a)``       |
+--------------------------------+--------------------------------+

**Solving equations and inverting matrices**

+--------------------------------+--------------------------------+
|        Python 3 - numpy        |        C++ 14 - xtensor        |
+================================+================================+
| ``np.linalg.inv(a)``           | ``xt::linalg::inv(a)``         |
+--------------------------------+--------------------------------+
| ``np.linalg.pinv(a)``          | ``xt::linalg::pinv(a)``        |
+--------------------------------+--------------------------------+
| ``np.linalg.solve(A, b)``      | ``xt::linalg::solve(A, b)``    |
+--------------------------------+--------------------------------+
| ``np.linalg.lstsq(A, b)``      | ``xt::linalg::lstsq(A, b)``    |
+--------------------------------+--------------------------------+


.. _`xtensor-blas`: https://github.com/QuantStack/xtensor-blas

