.. _tutconditions:

==========
Conditions
==========

IfElse vs Switch
================


- Both ops build a condition over symbolic variables.
- ``IfElse`` takes a *boolean* condition and two variables as inputs.
- ``Switch`` takes a *tensor* as condition and two variables as inputs.
  ``switch`` is an elementwise operation and is thus more general than ``ifelse``.
- Whereas ``switch`` evaluates both *output* variables, ``ifelse`` is lazy and only
  evaluates one variable with respect to the condition.

**Example**


.. testcode::

   from pytensor import tensor as pt
   from pytensor.ifelse import ifelse
   import pytensor, time, numpy

   a,b = pt.scalars('a', 'b')
   x,y = pt.matrices('x', 'y')

   z_switch = pt.switch(pt.lt(a, b), pt.mean(x), pt.mean(y))
   z_lazy = ifelse(pt.lt(a, b), pt.mean(x), pt.mean(y))

   f_switch = pytensor.function([a, b, x, y], z_switch,
                              mode=pytensor.compile.mode.Mode(linker='vm'))
   f_lazyifelse = pytensor.function([a, b, x, y], z_lazy,
                                  mode=pytensor.compile.mode.Mode(linker='vm'))

   val1 = 0.
   val2 = 1.
   big_mat1 = numpy.ones((10000, 1000))
   big_mat2 = numpy.ones((10000, 1000))

   n_times = 10

   tic = time.perf_counter()
   for i in range(n_times):
       f_switch(val1, val2, big_mat1, big_mat2)
   print('time spent evaluating both values %f sec' % (time.perf_counter() - tic))

   tic = time.perf_counter()
   for i in range(n_times):
       f_lazyifelse(val1, val2, big_mat1, big_mat2)
   print('time spent evaluating one value %f sec' % (time.perf_counter() - tic))

.. testoutput::
   :hide:
   :options: +ELLIPSIS

   time spent evaluating both values ... sec
   time spent evaluating one value ... sec

In this example, the ``IfElse`` op spends less time (about half as much) than ``Switch``
since it computes only one variable out of the two.

.. code-block:: none

   $ python ifelse_switch.py
   time spent evaluating both values 0.6700 sec
   time spent evaluating one value 0.3500 sec

Unless ``linker='vm'`` or ``linker='cvm'`` are used, ``ifelse`` will compute both
variables and take the same computation time as ``switch``. Although the linker
is not currently set by default to ``cvm``, it will be in the near future.

There is no automatic rewrite replacing a ``switch`` with a
broadcasted scalar to an ``ifelse``, as this is not always faster. See
this `ticket <http://www.assembla.com/spaces/theano/tickets/764>`_.

