
.. currentmodule:: tensor

===========================
Basic Tensor Functionality
===========================

Theano supports any kind of Python object, but its focus is support for
symbolic matrix expressions.  When you type,

>>> x = T.fmatrix()

the ``x`` is a :class:`TensorVariable` instance.
The ``T.fmatrix`` object itself is an instance of :class:`TensorType`. 
Theano knows what type of variable ``x`` is because ``x.type``
points back to ``T.fmatrix``.

This chapter explains the various ways of creating tensor variables,
the attributes and methods of :class:`TensorVariable` and :class:`TensorType`,
and various basic symbolic math and arithmetic that Theano supports for
tensor variables.

.. _libdoc_tensor_creation:

Creation
========

Theano provides a list of predefined tensor types that can be used
to create a tensor variables.  Variables can be named to facilitate debugging,
and all of these constructors accept an optional ``name`` argument.
For example, the following each produce a TensorVariable instance that stands
for a 0-dimensional ndarray of integers with the name ``'myvar'``:

>>> x = scalar('myvar', dtype='int32')
>>> x = iscalar('myvar')
>>> x = TensorType(dtype='int32', broadcastable=())('myvar')

Constructors with optional dtype
----------------------------------------

These are the simplest and often-preferred methods for creating symbolic
variables in your code.  By default, they produce floating-point variables
(with dtype determined by config.floatX, see :attr:`floatX`) so if you use
these constructors it is easy to switch your code between different levels of
floating-point precision.

.. function:: scalar(name=None, dtype=config.floatX)

    Return a Variable for a 0-dimensional ndarray

.. function:: vector(name=None, dtype=config.floatX)

    Return a Variable for a 1-dimensional ndarray

.. function:: row(name=None, dtype=config.floatX)

    Return a Variable for a 2-dimensional ndarray
    in which the number of columns is guaranteed to be 1.

.. function:: col(name=None, dtype=config.floatX)

    Return a Variable for a 2-dimensional ndarray
    in which the number of columns is guaranteed to be 1.

.. function:: matrix(name=None, dtype=config.floatX)

    Return a Variable for a 2-dimensional ndarray

.. function:: tensor3(name=None, dtype=config.floatX)

    Return a Variable for a 3-dimensional ndarray

.. function:: tensor4(name=None, dtype=config.floatX)

    Return a Variable for a 4-dimensional ndarray

.. #COMMENT
    Each of the types described above can be constructed by two methods:
    a singular version (e.g., :ref:`dmatrix <libdoc_tensor_creation>`)
    and a plural version (:ref:`dmatrices <libdoc_tensor_creation>`).
    When called, the singular version takes a single
    argument which is the name of the *Variable* we want to make and it
    makes a single Variable of that type. The plural version can either take


All Fully-Typed Constructors
----------------------------

The following TensorType instances are provided in the theano.tensor module.
They are all callable, and accept an optional ``name`` argument.  So for example:

.. code-block:: python

   from theano.tensor import *

   x = dmatrix()        # creates one Variable with no name
   x = dmatrix('x')     # creates one Variable with name 'x'
   xyz = dmatrix('xyz') # creates one Variable with name 'xyz'

.. #COMMENT
    table generated by 
    $ python Theano/doc/generate_dtype_tensor_table.py

============ =========== ==== =========== =================================
Constructor  dtype       ndim shape       broadcastable
============ =========== ==== =========== =================================
bscalar      int8        0    ()          ()                  
bvector      int8        1    (?,)        (False,)            
brow         int8        2    (1,?)       (True, False)       
bcol         int8        2    (?,1)       (False, True)       
bmatrix      int8        2    (?,?)       (False, False)      
btensor3     int8        3    (?,?,?)     (False, False, False)
btensor4     int8        4    (?,?,?,?)   (False, False, False, False)
wscalar      int16       0    ()          ()                  
wvector      int16       1    (?,)        (False,)            
wrow         int16       2    (1,?)       (True, False)       
wcol         int16       2    (?,1)       (False, True)       
wmatrix      int16       2    (?,?)       (False, False)      
wtensor3     int16       3    (?,?,?)     (False, False, False)
wtensor4     int16       4    (?,?,?,?)   (False, False, False, False)
iscalar      int32       0    ()          ()                  
ivector      int32       1    (?,)        (False,)            
irow         int32       2    (1,?)       (True, False)       
icol         int32       2    (?,1)       (False, True)       
imatrix      int32       2    (?,?)       (False, False)      
itensor3     int32       3    (?,?,?)     (False, False, False)
itensor4     int32       4    (?,?,?,?)   (False, False, False, False)
lscalar      int64       0    ()          ()                  
lvector      int64       1    (?,)        (False,)            
lrow         int64       2    (1,?)       (True, False)       
lcol         int64       2    (?,1)       (False, True)       
lmatrix      int64       2    (?,?)       (False, False)      
ltensor3     int64       3    (?,?,?)     (False, False, False)
ltensor4     int64       4    (?,?,?,?)   (False, False, False, False)
dscalar      float64     0    ()          ()                  
dvector      float64     1    (?,)        (False,)            
drow         float64     2    (1,?)       (True, False)       
dcol         float64     2    (?,1)       (False, True)       
dmatrix      float64     2    (?,?)       (False, False)      
dtensor3     float64     3    (?,?,?)     (False, False, False)
dtensor4     float64     4    (?,?,?,?)   (False, False, False, False)
fscalar      float32     0    ()          ()                  
fvector      float32     1    (?,)        (False,)            
frow         float32     2    (1,?)       (True, False)       
fcol         float32     2    (?,1)       (False, True)       
fmatrix      float32     2    (?,?)       (False, False)      
ftensor3     float32     3    (?,?,?)     (False, False, False)
ftensor4     float32     4    (?,?,?,?)   (False, False, False, False)
cscalar      complex64   0    ()          ()                  
cvector      complex64   1    (?,)        (False,)            
crow         complex64   2    (1,?)       (True, False)       
ccol         complex64   2    (?,1)       (False, True)       
cmatrix      complex64   2    (?,?)       (False, False)      
ctensor3     complex64   3    (?,?,?)     (False, False, False)
ctensor4     complex64   4    (?,?,?,?)   (False, False, False, False)
zscalar      complex128  0    ()          ()                  
zvector      complex128  1    (?,)        (False,)            
zrow         complex128  2    (1,?)       (True, False)       
zcol         complex128  2    (?,1)       (False, True)       
zmatrix      complex128  2    (?,?)       (False, False)      
ztensor3     complex128  3    (?,?,?)     (False, False, False)
ztensor4     complex128  4    (?,?,?,?)   (False, False, False, False)
============ =========== ==== =========== =================================

Plural Constructors 
--------------------------

There are several constructors that can produce multiple variables at once.
These are not frequently used in practice, but often used in tutorial examples to save space!

.. function:: iscalars, lscalars, fscalars, dscalars

    Return one or more scalar variables.

.. function:: ivectors, lvectors, fvectors, dvectors

    Return one or more vector variables.

.. function:: irows, lrows, frows, drows

    Return one or more row variables.

.. function:: icols, lcols, fcols, dcols

    Return one or more col variables.

.. function:: imatrices, lmatrices, fmatrices, dmatrices

    Return one or more matrix variables.

Each of these plural constructors accepts 
an integer or several strings. If an integer is provided, the method
will return that many Variables and if strings are provided, it will
create one Variable for each string, using the string as the Variable's
name. For example:

.. code-block:: python

   from theano.tensor import *

   x, y, z = dmatrices(3) # creates three matrix Variables with no names
   x, y, z = dmatrices('x', 'y', 'z') # creates three matrix Variables named 'x', 'y' and 'z'


Custom tensor types
-------------------

If you would like to construct a tensor variable with a non-standard
broadcasting pattern, or a larger number of dimensions you'll need to create
your own :class:`TensorType` instance.  You create such an instance by passing
the dtype and broadcasting pattern to the constructor.  For example, you
can create your own 5-dimensional tensor type

>>> dtensor5 = TensorType('float64', (False,)*5)
>>> x = dtensor5()
>>> z = dtensor5('z')

You can also redefine some of the provided types and they will interact
correctly:

>>> my_dmatrix = TensorType('float64', (False,)*2)
>>> x = my_dmatrix()       # allocate a matrix variable
>>> my_dmatrix == dmatrix  # this compares True

See :class:`TensorType` for more information about creating new types of
Tensor.


Converting from Python Objects
-------------------------------

Another way of creating a TensorVariable (a TensorSharedVariable to be
precise) is by calling :func:`shared()`

.. code-block:: python

    x = shared(numpy.random.randn(3,4))

This will return a :term:`shared variable <shared variable>` whose ``.value`` is
a numpy ndarray.  The number of dimensions and dtype of the Variable are
inferred from the ndarray argument.  The argument to `shared` *will not be
copied*, and subsequent changes will be reflected in ``x.value``.

For additional information, see the :func:`shared() <shared.shared>` documentation.

.. _libdoc_tensor_autocasting:

Finally, when you use a numpy ndarry or a Python number together with
:class:`TensorVariable` instances in arithmetic expressions, the result is a
:class:`TensorVariable`. What happens to the ndarray or the number?
Theano requires that the inputs to all expressions be Variable instances, so
Theano automatically wraps them in a :class:`TensorConstant`.

.. note:: 

    Theano makes a copy of any ndarray that you use in an expression, so
    subsequent
    changes to that ndarray will not have any effect on the Theano expression.

For numpy ndarrays the dtype is given, but the broadcastable pattern must be
inferred.  The TensorConstant is given a type with a matching dtype,
and a broadcastable pattern with a ``True`` for every shape dimension that is 1.

For python numbers, the broadcastable pattern is ``()`` but the dtype must be
inferred.  Python integers are stored in the smallest dtype that can hold
them, so small constants like ``1`` are stored in a ``bscalar``.
Likewise, Python floats are stored in an fscalar if fscalar suffices to hold
them perfectly, but a dscalar otherwise.

.. note::

    When config.floatX==float32 (see :mod:`config`), then Python floats
    are stored instead as single-precision floats.

    For fine control of this rounding policy, see
    theano.tensor.basic.autocast_float.

.. function:: as_tensor_variable(x, name=None, ndim=None)

    Turn an argument `x` into a TensorVariable or TensorConstant.

    Many tensor Ops run their arguments through this function as
    pre-processing.  It passes through TensorVariable instances, and tries to
    wrap other objects into TensorConstant.

    When `x` is a Python number, the dtype is inferred as described above.

    When `x` is a `list` or `tuple` it is passed through numpy.asarray

    If the `ndim` argument is not None, it must be an integer and the output
    will be broadcasted if necessary in order to have this many dimensions.  

    :rtype: :class:`TensorVariable` or :class:`TensorConstant`



TensorType and TensorVariable
=============================

.. class:: TensorType(Type)

    The Type class used to mark Variables that stand for `numpy.ndarray`
    values.  Recalling to the tutorial, the purple box in
    :ref:`the tutorial's graph-structure figure <tutorial-graphfigure>` is an instance of this class.

    .. attribute:: broadcastable
        
        A tuple of True/False values, one for each dimension.  True in
        position 'i' indicates that at evaluation-time, the ndarray will have
        size 1 in that 'i'-th dimension.  Such a dimension is called a
        *broadcastable dimension* (see :ref:`libdoc_tensor_broadcastable`).

        The broadcastable pattern indicates both the number of dimensions and
        whether a particular dimension must have length 1.

        Here is a table mapping some `broadcastable` patterns to what they
        mean:

        ===================== =================================
        pattern               interpretation
        ===================== =================================
        []                    scalar
        [True]                1D scalar (vector of length 1)
        [True, True]          2D scalar (1x1 matrix)
        [False]               vector
        [False, False]        matrix
        [False] * n           nD tensor
        [True, False]         row (1xN matrix)
        [False, True]         column (Mx1 matrix)
        [False, True, False]  A Mx1xP tensor (a)
        [True, False, False]  A 1xNxP tensor (b)
        [False, False, False] A MxNxP tensor (pattern of a + b)
        ===================== =================================

        For dimensions in which broadcasting is False, the length of this
        dimension can be 1 or more.  For dimensions in which broadcasting is True,
        the length of this dimension must be 1.

        When two arguments to an element-wise operation (like addition or
        subtraction) have a different
        number of dimensions, the broadcastable
        pattern is *expanded to the left*, by padding with ``True``. For example,
        a vector's pattern, ``[False]``, could be expanded to ``[True, False]``, and
        would behave like a row (1xN matrix). In the same way, a matrix (``[False,
        False]``) would behave like a 1xNxP tensor (``[True, False, False]``).

        If we wanted to create a type representing a matrix that would
        broadcast over the middle dimension of a 3-dimensional tensor when
        adding them together, we would define it like this:

        >>> middle_broadcaster = TensorType('complex64', [False, True, False])
        
    .. attribute:: ndim

        The number of dimensions that a Variable's value will have at
        evaluation-time.  This must be known when we are building the
        expression graph.

    .. attribute:: dtype

        A string indicating
        the numerical type of the ndarray for which a Variable of this Type
        is standing.

        .. _dtype_list: 

        The dtype attribute of a TensorType instance can be any of the
        following strings.

        ================= =================== =================
        dtype             domain              bits
        ================= =================== =================
        ``'int8'``        signed integer      8
        ``'int16'``       signed integer      16
        ``'int32'``       signed integer      32
        ``'int64'``       signed integer      64
        ``'uint8'``       unsigned integer    8
        ``'uint16'``      unsigned integer    16
        ``'uint32'``      unsigned integer    32
        ``'uint64'``      unsigned integer    64
        ``'float32'``     floating point      32
        ``'float64'``     floating point      64
        ``'complex64'``   complex             64 (two float32)
        ``'complex128'``  complex             128 (two float64)
        ================= =================== =================

    .. method:: __init__(self, dtype, broadcastable)

        If you wish to use a type of tensor which is not already available 
        (for example, a 5D tensor) you can build an appropriate type by instantiating
        :class:`TensorType`.


TensorVariable
----------------

.. class:: TensorVariable(Variable, _tensor_py_operators)

    The result of symbolic operations typically have this type.

    See :class:`_tensor_py_operators` for most of the attributes and methods
    you'll want to call.

.. class:: TensorConstant(Variable, _tensor_py_operators)

    Python and numpy numbers are wrapped in this type.

    See :class:`_tensor_py_operators` for most of the attributes and methods
    you'll want to call.

.. class:: TensorSharedVariable(Variable, _tensor_py_operators)

    This type is returned by :func:`shared` when the value to share is a numpy
    ndarray.

    See :class:`_tensor_py_operators` for most of the attributes and methods
    you'll want to call.


.. class:: _tensor_py_operators(object)

    This mix-in class adds convenient attributes, methods, and support for Python operators (see :ref:`tensor_operator_support`).

    .. attribute:: type

        A reference to the :class:`TensorType` instance describing the sort of
        values that might be associated with this variable.

    .. attribute:: ndim

        The number of dimensions of this tensor.  Aliased to
        :attr:`TensorType.ndim`.

    .. attribute:: dtype

        The numeric type of this tensor. Aliased to 
        :attr:`TensorType.dtype`.

    .. method:: reshape(shape, ndim=None)

        Returns a view of this tensor that has been reshaped as in
        numpy.reshape.  If the shape is a Variable argument, then you might
        need to use the optional `ndim` parameter to declare how many elements
        the shape has, and therefore how many dimensions the reshaped Variable
        will have.

        See :func:`reshape`.

    .. method:: dimshuffle(*pattern)

        Returns a view of this tensor with permuted dimensions.  Typically the
        pattern will include the integers 0, 1, ... ndim-1, and any number of
        'x' characters in dimensions where this tensor should be broadcasted.

        A few examples of patterns and their effect:

            ('x') -> make a 0d (scalar) into a 1d vector
            (0, 1) -> identity for 2d vectors
            (1, 0) -> inverts the first and second dimensions 
            ('x', 0) -> make a row out of a 1d vector (N to 1xN)
            (0, 'x') -> make a column out of a 1d vector (N to Nx1)
            (2, 0, 1) -> AxBxC to CxAxB
            (0, 'x', 1) -> AxB to Ax1xB
            (1, 'x', 0) -> AxB to Bx1xA


        See :func:`dimshuffle`.

        (The above link just points back to this paragraph. Maybe whoever
        wrote that meant to refer to theano.tensor.DimShuffle)

        


    .. method:: flatten(ndim=1)

        Returns a view of this tensor with `ndim` dimensions, whose shape for the first
        `ndim-1` dimensions will be the same as `self`, and shape in the
        remaining dimension will be expanded to fit in all the data from self.

        See :func:`flatten`.

    .. attribute:: T

        Transpose of this tensor.

        >>> x = T.zmatrix()
        >>> y = 3+.2j * x.T 

        .. note::
            
            In numpy and in Theano, the transpose of a vector is exactly the
            same vector!  Use `reshape` or `dimshuffle` to turn your vector
            into a row or column matrix.




Shaping and Shuffling
=====================

To re-order the dimensions of a variable, to insert or remove broadcastable
dimensions, see :meth:`_tensor_py_operators.dimshuffle`

.. function:: shape(x)

    Returns lvector representing shape of `x`

.. function:: reshape(x, newshape, ndim=None)

    :type x: any TensorVariable (or compatible)
    :param x: variable to be reshaped

    :type newshape: lvector (or compatible)
    :param newshape: the new shape for `x`

    :param ndim: optional - the length that `newshape`'s value will have.
        If this is ``None``, then `reshape()` will infer it from `newshape`.

    :rtype: variable with x's dtype, but ndim dimensions

    .. note:: 

        This function can infer the length of a symbolic newshape in some
        cases, but if it cannot and you do not provide the `ndim`, then this
        function will raise an Exception.
    

.. function:: shape_padleft(x, n_ones=1)

    Reshape `x` by left padding the shape with `n_ones` 1s. Note that all 
    this new dimension will be broadcastable. To make them non-broadcastable 
    see the :func:`unbroadcast`.

    :param x: variable to be reshaped 
    :type x: any TensorVariable (or compatible)

    :type n_ones: int
    :type n_ones: number of dimension to be added to `x`



.. function:: shape_padright(x,n_ones = 1)

    Reshape `x` by right padding the shape with `n_ones` 1s. Note that all 
    this new dimension will be broadcastable. To make them non-broadcastable 
    see the :func:`unbroadcast`.

    :param x: variable to be reshaped 
    :type x: any TensorVariable (or compatible)

    :type n_ones: int
    :type n_ones: number of dimension to be added to `x`

.. function:: unbroadcast(x, *axes)

    Make `x` impossible to broadcast in the specified axes `axes`. For
    example, `unbroadcast(x,0)` will make the first dimension of `x`
    unbroadcastable.

.. function:: addbroadcast(x, *axes)

    Make `x` broadcastable in the specified axes `axes`. For
    example, `unbroadcast(x,0)` will make the first dimension of `x`
    broadcastable.

.. function:: flatten(x, outdim=1)
    
    Similar to :func:`reshape`, but the shape is inferred from the shape of `x`.

    :param x: variable to be flattened
    :type x: any TensorVariable (or compatible)

    :type outdim: int
    :param outdim: the number of dimensions in the returned variable

    :rtype: variable with same dtype as `x` and `outdim` dimensions
    :returns: variable with the same shape as `x` in the leading `outdim-1`
        dimensions, but with all remaining dimensions of `x` collapsed into
        the last dimension.

    For example, if we flatten a tensor of shape (2,3,4,5) with flatten(x,
    outdim=2), then we'll have the same (2-1=1) leading dimensions (2,), and the
    remaining dimensions are collapsed.  So the output in this example would
    have shape (2, 60).


.. function:: zeros_like(x)

    :param x: tensor that has same shape as output

    Returns a tensor filled with 0s that has same shape as `x`.

.. function:: ones_like(x)

    :param x: tensor that has same shape as output

    Returns a tensor filled with 1s that has same shape as `x`.

.. function:: fill(a,b)

    :param a: tensor that has same shape as output 
    :param b: theano scalar or value with which you want to fill the output

    Create a matrix by filling the shape of `a` with `b`

.. function:: eye(n, m = None, k = 0, dtype=theano.config.floatX)

    :param n: number of rows in output (value or theano scalar)
    :param m: number of columns in output (value or theano scalar)
    :param k: Index of the diagonal: 0 refers to the main diagonal, 
              a positive value refers to an upper diagonal, and a 
              negative value to a lower diagonal. It can be a theano 
              scalar.
    :returns: An array where all elements are equal to zero, except for the `k`-th
              diagonal, whose values are equal to one.

.. function:: identity_like(x)

    :param x: tensor
    :returns: A tensor of same shape as `x` that is filled with 0s everywhere
              except for the main diagonal, whose values are equal to one. The output
              will have same dtype as `x`.

.. function:: stack(*tensors)

    Return a Tensor representing for the arguments all stacked up into a single Tensor.
    (of 1 rank greater).

    :param tensors: one or more tensors of the same rank
    :returns: A tensor such that rval[0] == tensors[0], rval[1] == tensors[1], etc.

    >>> x0 = T.scalar()
    >>> x1 = T.scalar()
    >>> x2 = T.scalar()
    >>> x = T.stack(x0, x1, x2)
    >>> # x.ndim == 1, is a vector of length 3.

.. function:: concatenate(tensor_list, axis=0)

    :type tensor_list: a list or tuple of Tensors that all have the same shape in the axes
                        *not* specified by the `axis` argument.
    :param tensor_list: one or more Tensors to be concatenated together into one.
    :type axis: literal or symbolic integer
    :param axis: Tensors will be joined along this axis, so they may have different
        ``shape[axis]``

    >>> x0 = T.fmatrix()
    >>> x1 = T.ftensor3()
    >>> x2 = T.fvector()
    >>> x = T.concatenate([x0, x1[0], T.shape_padright(x2)], axis=1)
    >>> # x.ndim == 2


Reductions 
==========


.. function:: max(x, axis=None)

    :Parameter: *x* - symbolic Tensor (or compatible)
    :Parameter: *axis* - axis along which to compute the maximum
    :Returns: the maximum value along a given axis
    :note: see maximum for elemwise max

    If axis=None, then axis is assumed to be ndim(x)-1

.. function:: min(x, axis=None)

    :Parameter: *x* - symbolic Tensor (or compatible)
    :Parameter: *axis* - axis along which to compute the minimum
    :Returns: the minimum value along a given axis
    :note: see miminum for elemwise min

    if axis=None, then axis is assumed to be ndim(x)-1

.. function:: sum(x, axis=None)

    :Parameter: *x* -  symbolic Tensor (or compatible)
    :Parameter: *axis* - axis or axes along which to compute the sum
    :Returns: sum of *x* along *axis*

    axis can be:
     * *None* - in which case the sum is computed along all axes (like numpy)
     * an *int* - computed along this axis
     * a *list of ints* - computed along these axes

.. function:: prod(x, axis=None)

    :Parameter: *x* -  symbolic Tensor (or compatible)
    :Parameter: *axis* - axis or axes along which to compute the product
    :Returns: product of every term in *x* along *axis*

    axis can be:
     * *None* - in which case the sum is computed along all axes (like numpy)
     * an *int* - computed along this axis
     * a *list of ints* - computed along these axes

.. function:: mean(x, axis=None)

    :Parameter: *x* -  symbolic Tensor (or compatible)
    :Parameter: *axis* - axis or axes along which to compute the mean
    :Returns: mean value of *x* along *axis*

    axis can be:
     * *None* - in which case the mean is computed along all axes (like numpy)
     * an *int* - computed along this axis
     * a *list of ints* - computed along these axes

.. function:: var(x, axis=None)

    :Parameter: *x* -  symbolic Tensor (or compatible)
    :Parameter: *axis* - axis or axes along which to compute the variance
    :Returns: variance of *x* along *axis*

    axis can be:
     * *None* - variance computed along all axes (like numpy)
     * an *int* - computed along this axis
     * a *list of ints* - computed along these axes


Indexing
========

Like numpy, Theano distinguishes between *basic* and *advanced* indexing.
Theano fully supports basic indexing
(see `numpy's basic indexing  <http://docs.scipy.org/doc/numpy/reference/arrays.indexing.html>`_).

Advanced indexing is almost entirely unsupported (for now).
The one sort of advanced indexing that is supported is the retrieval of the c[i]'th element of each
row of a matrix x:

>>> x = T.fmatrix()
>>> c = T.lvector()
>>> x[T.arange(c.shape[0]), c]


Index-assignment is *not* supported.  If you want to do something like ``a[5]
= b`` or ``a[5]+=b``, see :func:`setsubtensor` and :func:`incsubtensor` below.

.. autofunction:: theano.tensor.basic.setsubtensor

.. autofunction:: theano.tensor.basic.incsubtensor

.. _tensor_operator_support:

Operator Support
================

Many Python operators are supported.

>>> a, b = T.itensor3(), T.itensor3() # example inputs

Arithmetic
--------------

>>> a + 3      # T.add(a, 3) -> itensor3
>>> 3 - a      # T.sub(3, a)
>>> a * 3.5    # T.mul(a, 3.5) -> ftensor3 or dtensor3 (depending on casting)
>>> 2.2 / a    # T.truediv(2.2, a)
>>> 2.2 // a   # T.intdiv(2.2, a)
>>> 2.2**a     # T.pow(2.2, a)
>>> b % a      # T.mod(b, a)

Bitwise
-------------

>>> a & b      # T.and_(a,b)    bitwise and
>>> a ^ 1      # T.xor(a,1)     bitwise xor
>>> a | b      # T.or_(a,b)     bitwise or
>>> ~a         # T.invert(a)    bitwise invert

Inplace
-------------

In-place operators are *not* supported.  Theano's graph-optimizations
will determine which intermediate values to use for in-place
computations.  If you would like to update the value of a
:term:`shared variable`, consider using the ``updates`` argument to
:func:`theano.function`.

.. _libdoc_tensor_elementwise:

Elementwise
===========

Casting
-------

.. function:: cast(x, dtype)

    Cast any tensor `x` to a Tensor of the same shape, but with a different
    numerical type `dtype`.
    
    This is not a reinterpret cast, but a coersion cast, similar to
    ``numpy.asarray(x, dtype=dtype)``.

    .. code-block:: python

        import theano.tensor as T
        x_as_float = T.matrix()
        x_as_int = T.cast(x, 'int32')


    Attempting to casting a complex value to a real value is ambiguous and
    will raise an exception.  Use `real()`, `imag()`, `abs()`, or `angle()`.

.. function:: real(x)

    Return the real (not imaginary) components of Tensor x. 
    For non-complex `x` this function returns x.

.. function:: imag(x)

    Return the imaginary components of Tensor x. 
    For non-complex `x` this function returns zeros_like(x).


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

The six usual equality and inequality operators share the same interface.
  :Parameter:  *a* - symbolic Tensor (or compatible)
  :Parameter:  *b* - symbolic Tensor (or compatible)
  :Return type: symbolic Tensor
  :Returns: a symbolic tensor representing the application of the logical elementwise operator.

  .. note::

    Theano has no boolean dtype.  Instead, all boolean tensors are represented
    in ``'int8'``.

  Here is an example with the less-than operator.

  .. code-block:: python 

    import theano.tensor as T
    x,y = T.dmatrices('x','y')
    z = T.le(x,y)

.. function:: lt(a, b)

    Returns a symbolic ``'int8'`` tensor representing the result of logical less-than (a<b).

    Also available using syntax ``a < b``

.. function:: gt(a, b)

    Returns a symbolic ``'int8'`` tensor representing the result of logical greater-than (a>b).

    Also available using syntax ``a > b``

.. function:: le(a, b)

    Returns a variable representing the result of logical less than or equal (a<=b).

    Also available using syntax ``a <= b``

.. function:: ge(a, b)

    Returns a variable representing the result of logical greater or equal than (a>=b).

    Also available using syntax ``a >= b``

.. function:: eq(a, b)

    Returns a variable representing the result of logical equality (a==b).

.. function:: neq(a, b)

    Returns a variable representing the result of logical inequality (a!=b).


Condition
---------

.. function:: switch(cond, ift, iff)

    Returns a variable representing a switch between ift (iftrue) and iff (iffalse)
     based on the condition cond. This is the theano equivalent of numpy.where.

      :Parameter:  *cond* - symbolic Tensor (or compatible)
      :Parameter:  *ift* - symbolic Tensor (or compatible)
      :Parameter:  *iff* - symbolic Tensor (or compatible)
      :Return type: symbolic Tensor

    .. code-block:: python 

      import theano.tensor as T
      a,b = T.dmatrices('a','b')
      x,y = T.dmatrices('x','y')
      z = T.switch(T.lt(a,b), x, y)

.. function:: clip(x, min, max)

    Return a variable representing x, but with all elements greater than
    `max` clipped to `max` and all elements less than `min` clipped to `min`.

    Normal broadcasting rules apply to each of `x`, `min`, and `max`.

Bit-wise
--------


The bitwise operators possess this interface: 
    :Parameter:  *a* - symbolic Tensor of integer type.
    :Parameter:  *b* - symbolic Tensor of integer type.

    .. note:: 

        The bitwise operators must have an integer type as input.
    
        The bit-wise not (invert) takes only one parameter.

    :Return type: symbolic Tensor with corresponding dtype.

.. function:: and_(a, b)

    Returns a variable representing the result of the bitwise and.

.. function:: or_(a, b)

    Returns a variable representing the result of the bitwise or.

.. function:: xor(a, b)

    Returns a variable representing the result of the bitwise xor.

.. function:: invert(a)

    Returns a variable representing the result of the bitwise not.

Here is an example using the bit-wise ``and_`` via the ``&`` operator:

.. code-block:: python 

    import theano.tensor as T
    x,y = T.imatrices('x','y')
    z = x & y


Mathematical
------------

.. function:: abs_(a)

    Returns a variable representingthe absolute of a, ie ``|a|``.

    .. note:: Can also be accessed with ``abs(a)``.

.. function:: angle(a)

    Returns a variable representing angular component of complex-valued Tensor `a`.

.. function:: exp(a)

    Returns a variable representing the exponential of a, ie e^a.

.. function:: maximum(a,b)

   Returns a variable representing the maximum element by element of a and b

.. function:: minimum(a,b)

   Returns a variable representing the minimum element by element of a and b

.. function:: neg(a)

    Returns a variable representing the negation of `a` (also ``-a``).

.. function:: inv(a)

    Returns a variable representing the inverse of a, ie 1.0/a. Also called reciprocal.

.. function:: log(a), log2(a), log10(a)

    Returns a variable representing the base e, 2 or 10 logarithm of a.

.. function:: sgn(a)

    Returns a variable representing the sign of a.

.. function:: ceil(a)

    Returns a variable representing the ceiling of a (for example ceil(2.1) is 3).

.. function:: floor(a)

    Returns a variable representing the floor of a (for example floor(2.9) is 2).

.. function:: round(a, mode="half_away_from_zero")

    Returns a variable representing the rounding of a in the same dtype as a. Implemented rounding mode are half_away_from_zero and half_to_even.

.. function:: iround(a, mode="half_away_from_zero")

    Short hand for cast(round(a, mode),'int64').

.. function:: sqr(a)

    Returns a variable representing the square of a, ie a^2.

.. function:: sqrt(a)

    Returns a variable representing the of a, ie a^0.5.

.. function:: cos(a), sin(a), tan(a)

    Returns a variable representing the trigonometric functions of a (cosine, sine and tangent).

.. function:: cosh(a), sinh(a), tanh(a)

    Returns a variable representing the hyperbolic trigonometric functions of a (hyperbolic cosine, sine and tangent).

.. function:: erf(a), erfc(a)

    Returns a variable representing the error function or the complementary error function.


.. _libdoc_tensor_broadcastable:

Broadcasting in Theano vs. Numpy
--------------------------------

Broadcasting is a mechanism which allows tensors with
different numbers of dimensions to be added or multiplied
together by (virtually) replicating the smaller tensor along
the dimensions that it is lacking.

Broadcasting is the mechanism by which a scalar
may be added to a matrix, a vector to a matrix or a scalar to
a vector.

.. figure:: bcast.png

Broadcasting a row matrix. T and F respectively stand for
True and False and indicate along which dimensions we allow
broadcasting.

If the second argument were a vector, its shape would be
``(2,)`` and its broadcastable pattern ``(F,)``. They would
be automatically expanded to the **left** to match the
dimensions of the matrix (adding ``1`` to the shape and ``T``
to the pattern), resulting in ``(1, 2)`` and ``(T, F)``.
It would then behave just like the example above.


Unlike numpy which does broadcasting dynamically, Theano needs
to know, for any operation which supports broadcasting, which
dimensions will need to be broadcasted. When applicable, this
information is given in the :ref:`type` of a *Variable*.

See also:

* `SciPy documentation about numpy's broadcasting <http://www.scipy.org/EricsBroadcastingDoc>`_

* `OnLamp article about numpy's broadcasting <http://www.onlamp.com/pub/a/python/2000/09/27/numerically.html>`_





Linear Algebra
==============

.. function:: dot(X, Y)

    :param X: left term
    :param Y: right term
    :type X: symbolic matrix or vector
    :type Y: symbolic matrix or vector
    :rtype: symbolic matrix or vector
    :return: the inner product of `X` and `Y`.

.. function:: outer(X, Y)

    :param X: left term
    :param Y: right term
    :type X: symbolic vector
    :type Y: symbolic vector
    :rtype: symbolic matrix 

    :return: vector-vector outer product

.. function:: tensordot(X, Y, axes=2)

    This is a symbolic standing for ``numpy.tensordot``.

    :param X: left term
    :param Y: right term
    :param axes: sum out these axes from X and Y.
    :type X: symbolic tensor
    :type Y: symbolic tensor
    :rtype: symbolic tensor 
    :type axes: see numpy.tensordot

    :return: tensor product



Gradient / Differentiation
==========================

.. function:: grad(cost, wrt, g_cost=None, consider_constant=[], warn_type=False)

    Return symbolic gradients for one or more variables with respect to some
    cost.
    
    :type cost: 0-d tensor variable
    :type wrt: tensor variable or list of tensor variables
    :type g_cost: same as `cost`
    :type consider_constant: list of variables
    :type warn_type: bool

    :param cost: a scalar with respect to which we are differentiating
    :param wrt: term[s] for which we want gradients
    :param g_cost: the gradient on the cost
    :param consider_constant: variables whose gradients will be held at 0.
    :param warn_type: True will trigger warnings via the logging module when
       the gradient on an expression has a different type than the original
       expression

    :rtype: variable or list of variables (matching `wrt`)
    :returns: gradients with respect to cost for each of the `wrt` terms 




