Tensor basics
=============

Creating a tensor
-----------------
A tensor is nothing more than a multi-dimensional array.

Let's take for this example the tensor :math:`\tilde X` defined by its frontal slices:

.. math::

   X_1 =
   \left[
   \begin{matrix}
   0  & 2  & 4  & 6\\
   8  & 10 & 12 & 14\\
   16 & 18 & 20 & 22\\
   \end{matrix}
   \right]

   \text{and}

   X_2 =
   \left[
   \begin{matrix}
   1  & 3  & 5  & 7\\
   9  & 11 & 13 & 15\\
   17 & 19 & 21 & 23\\
   \end{matrix}
   \right]


In Python, this array can be expressed as a numpy array::

   >>> import numpy as np
   >>> import tensorly as tl
   >>> X = tl.tensor(np.arange(24).reshape((3, 4, 2)))


You can view the frontal slices by fixing the last axis::

   >>> X[..., 0]
   array([[ 0,  2,  4,  6],
          [ 8, 10, 12, 14],
          [16, 18, 20, 22]])

   >>> X[..., 1]
   array([[ 1,  3,  5,  7],
          [ 9, 11, 13, 15],
          [17, 19, 21, 23]])


Unfolding
---------
Also called **matrization**, **unfolding** a tensor is done by reading the element in a given way as to obtain a matrix instead of a tensor.

For a tensor of size :math:`(I_0, I_1, \cdots, I_N)`, the n-mode unfolding of this tensor will be of size :math:`(I_n, I_0, I_1 \times \cdots \times I_{n-1} \times I_{n+1} \cdots \times I_N)`.

.. important::
   In tensorly we use an unfolding different from the classical one as defined in [1]_ for better performance.

   Given a tensor :math:`\tilde X \in \mathbb{R}^{I_0, I_1 \times I_2 \times \cdots \times I_N}`, the
   mode-n unfolding of :math:`\tilde X` is a matrix :math:`\mathbf{X}_{[n]} \in \mathbb{R}^{I_n, I_M}`,
   with :math:`M = \prod_{\substack{k=0,\\k \neq n}}^N I_k` and is defined by
   the mapping from element :math:`(i_0, i_1, \cdots, i_N)` to :math:`(i_n, j)`, with

   .. math::
      j = \sum_{\substack{k=0,\\k \neq n}}^N i_k \times \prod_{\substack{m=k+1,\\ k \neq n}}^N I_m.


.. warning::

   Traditionally, mode-1 unfolding denotes the unfolding along the first dimension.
   However, to be consistent with the Python indexing that always starts at zero,
   in tensorly, unfolding also starts at zero!

   Therefore ``unfold(tensor, 0)`` will unfold said tensor along its first dimension!


For instance, using the :math:`\tilde X` previously defined, the 0-mode unfolding of :math:`\tilde X`:

.. math::

   \tilde X_{[0]} =
   \left[ \begin{matrix}
      0  & 1  & 2  & 3  &  4 & 5  & 6  & 7\\
      8  & 9  & 10 & 11 & 12 & 13 & 14 & 15\\
      16 & 17 & 18 & 19 & 20 & 21 & 22 & 23\\
   \end{matrix} \right]


The 1-mode unfolding is given by:

.. math::

   \tilde X_{[1]} =
   \left[ \begin{matrix}
      0 & 1 & 8  & 9  & 16 & 17\\
      2 & 3 & 10 & 11 & 18 & 19\\
      4 & 5 & 12 & 13 & 20 & 21\\
      6 & 7 & 14 & 15 & 22 & 23\\
   \end{matrix} \right]

Finally, the 2-mode unfolding is the unfolding along the last axis:

.. math::

   \tilde X_{[2]} =
   \left[ \begin{matrix}
      0 & 2 & 4 & 6 & 8 & 10 & 12 & 14 & 16 & 18 & 20 & 22\\
      1 & 3 & 5 & 7 & 9 & 11 & 13 & 15 & 17 & 19 & 21 & 23\\
   \end{matrix} \right]


In tensorly:

.. code-block:: python

   >>> from tensorly import unfold
   >>> unfold(X, 0) # mode-1 unfolding
   array([[ 0,  1,  2,  3,  4,  5,  6,  7],
          [ 8,  9, 10, 11, 12, 13, 14, 15],
          [16, 17, 18, 19, 20, 21, 22, 23]])

   >>> unfold(X, 1) # mode-2 unfolding
   array([[ 0,  1,  8,  9, 16, 17],
          [ 2,  3, 10, 11, 18, 19],
          [ 4,  5, 12, 13, 20, 21],
          [ 6,  7, 14, 15, 22, 23]])

   >>> unfold(X, 2) # mode-3 unfolding
   array([[ 0,  2,  4,  6,  8, 10, 12, 14, 16, 18, 20, 22],
          [ 1,  3,  5,  7,  9, 11, 13, 15, 17, 19, 21, 23]])

Folding
-------
You can **fold** an unfolded tensor back from matrix to full tensor using the :func:`tensorly.base.fold` function.

.. code-block:: python

   >>> from tensorly import fold
   >>> unfolding = unfold(X, 1)
   >>> original_shape = X.shape
   >>> fold(unfolding, 1, original_shape)
   array([[[ 0,  1],
        [ 2,  3],
        [ 4,  5],
        [ 6,  7]],

       [[ 8,  9],
        [10, 11],
        [12, 13],
        [14, 15]],

       [[16, 17],
        [18, 19],
        [20, 21],
        [22, 23]]])


References
----------
.. [1] T.G.Kolda and B.W.Bader, "Tensor Decompositions and Applications",
       SIAM REVIEW, vol. 51, n. 3, pp. 455-500, 2009.
