.. currentmodule:: brian.experimental.codegen2

.. index::
	pair: code; generation
	
Code generation
^^^^^^^^^^^^^^^

.. warning::
	This section is a work in progress, documenting the most recent code generation
	framework, which is in the package ``brian.experimental.codegen2``.

Overview
========

To generate code, we start with a basic statement or set of statements we want
to evaluate for all neurons, or for all synapses, and then apply various
transformations to generate code that will do this. We start from a
structured, language-invariant representation of the set of basic statements.
We then 'resolve' the unknown symbols in it. This is done recursively, the
resolution of each symbol can add vectorised statements or loops to the
current representation, and add data to a namespace that will be associated to
the final code. Symbols will be things like a :class:`~brian.NeuronGroup` state
variable, or a synaptic weight value. The output of this process is a new,
more complicated structured representation, including things like loops if
necessary. Next, we convert this structured representation into a code
string. Finally, this code string is JIT-compiled into an executable object.

Using numerical integration generation
--------------------------------------

You can use Brian's equations format to generate C/C++ code for a numerical
integration step, for example::

    eqs = '''
    dv/dt = (ge+gi-(v+49*mV))/(20*ms) : volt
    dge/dt = -ge/(5*ms) : volt
    dgi/dt = -gi/(10*ms) : volt
    '''
    code, vars, params = make_c_integrator(eqs, method=euler, dt=0.1*ms)
    print code

has output::

	double _temp_v = 50.0*ge + 50.0*gi - 50.0*v - 2.45;
	double _temp_ge = -200.0*ge;
	double _temp_gi = -100.0*gi;
	v += _temp_v*0.0001;
	ge += _temp_ge*0.0001;
	gi += _temp_gi*0.0001;

See the documentation for the function :func:`make_c_integrator`.

Using the code generation package
---------------------------------

The basic way to use the code generation module is as follows:

1. Create a :class:`Block` of :class:`Statement` objects which you want to
   execute. You can use :func:`statements_from_codestring` to do this.
2. Create a dictionary of :class:`Symbol` objects corresponding to the symbols
   in the block above.
3. Call :meth:`CodeItem.generate` with the specified language and symbols, to
   give you a :class:`Code` object.
4. Optionally, insert additional data into the namespace of the :class:`Code`
   object.
5. Use the :class:`Code` object via ``code(name1=val1, name2=val2)`` where the
   ``name=val`` are to be inserted into the namespace before the code is called.

This process is very clearly illustrated in the source code for
:class:`CodeGenStateUpdater`.

Structure of the package
------------------------

The following are the main elements of the code generation package:

:class:`Code`
	This is the output of the code generation package, a compilable/compiled
	code fragment, along with a namespace in which it is executed.
:class:`Language`
	Used to specify which language the output should have.
:class:`CodeItem`
	Before code is converted into a specific language, it is stored in a
	language-invariant format consisting of :class:`CodeItem` objects, which
	can in turn contain other :class:`CodeItem` objects. The main derived
	classes from this are :class:`Block` and :class:`Statement`. The first can
	contain a series of statements, or it can be a for loop, an if block, etc.
	A :class:`Statement` can be a :class:`MathematicalStatement` or
	:class:`CodeStatement`. The former is for things like ``x=y*z`` and the
	latter for things like ``x = arr[index]``.
:class:`Symbol`, :func:`resolve`
	A :class:`CodeItem` with unresolved dependencies needs to be resolved by
	the function :func:`resolve`. Each unresolved depdendency should correspond
	to a :class:`Symbol` which knows how to modify a :class:`CodeItem` in order
	to resolve itself. For example, a :class:`NeuronGroupStateVariableSymbol`
	will insert the :class:`~brian.NeuronGroup` state variable value into the
	namespace, create a new array symbol like ``__arr_V`` for symbol ``V``,
	and resolve itself either by doing nothing (in Python, as the variable is
	already vectorised), or by introducing a loop (in C++), or by setting the
	index variable as the kernel thread (for GPU). For more details, see
	the section on resolution below.
:func:`make_integration_step`, :func:`euler`, :func:`rk2`, :func:`exp_euler`
	Numerical integration schemes, each integration scheme (such as
	:func:`euler`) converts a set of differential equations into a sequence of
	:class:`MathematicalStatement` objects comprising an integration step.
:class:`CodeGenStateUpdater`, :class:`CodeGenThreshold`, :class:`CodeGenReset`, :class:`CodeGenConnection`
	Brian objects using code generation.
	
Resolution process
==================

Example
-------

We start with a worked example. Consider the statement::

	V = V+1
	
Here ``V`` is a :class:`NeuronGroup` state variable. We wish to transform this
into code that can be executed. In the case of Python, the output would look
like::

	_neuron_index = slice(None)
	V = _arr_V[_neuron_index]
	_arr_V[_neuron_index] = V+1
	
The symbol ``_arr_V`` would be added directly to the namespace.

In the case of C++ it would look like::

	for(int _neuron_index=0; _neuron_index<_len__arr_V; _neuron_index++)
	{
		double &V = _arr_V[_neuron_index];
		V = V+1;
	}

Here the symbols ``_arr_V`` and `_len__arr_V`` would be added to the namespace.
The reason for these complicated names is to do with making the code as
generic as possible, not introducing namespace clashes (symbols starting with
``_`` are reserved), etc.

The way the process works is we start with the statement ``V=V+1`` and a
:class:`Symbol` object with name ``V``, specifically a
:class:`NeuronGroupStateVariableSymbol`. The statement ``V=V+1`` depends on
``V`` with both a :class:`Read` and :class:`Write` dependency. We therefore
have to 'resolve' the symbol ``V``. To do this we call the method
:meth:`~ArraySymbol.resolve` on ``V``.

In the case of Python, this gives us::

	V = _arr_V[_neuron_index]
	_arr_V[_neuron_index] = V+1

It adds ``_arr_V`` to the namespace, and creates a dependency on
``_neuron_index``. The reason that ``V=V+1`` is translated to
``_arr_V[_neuron_index] = V+1`` is that on the left hand side we have a write
variable, and on the right hand side we have a read variable. In Python, when
vectorising, we have no choice but to give the underlying array with its slice
when writing to an array. However, at this point the code generation framework
doesn't know what ``_neuron_index`` will be, so it could be, for example, an
array of indices. In this case, suppose we did ``V*V`` it would be more
efficient to compute ``V=_arr_V[_neuron_index]`` and then compute ``V*V`` than
to compute ``_arr_V[_neuron_index]*_arr_V[_neuron_index]``, and in the case
where ``_neuron_index=slice(None)`` it is no slower, so we always do this.

In the case of C++, the first resolution step gives us::

	double &V = _arr_V[_neuron_index];
	V = V+1;

For the second resolution step, we need to resolve ``_neuron_index``, which
is a symbol of type :class:`SliceIndex`, telling us that ``_neuron_index``
varies over all neurons. Note that we could also have ``_neuron_index`` being
an :class:`ArrayIndex`, for examples ``spikes``, and then this could be used
for a reset operation (we would iterate only over those indices of neurons
which had spiked). Here though, we iterate over all neurons. In Python, calling
the :meth:`~ArrayIndex.resolve` method of ``_neuron_index`` gives us:: 

	_neuron_index = slice(None)
	V = _arr_V[_neuron_index]
	_arr_V[_neuron_index] = V+1

and in C++::

	for(int _neuron_index=0; _neuron_index<_len__arr_V; _neuron_index++)
	{
		double &V = _arr_V[_neuron_index];
		V = V+1;
	}

In both cases, the ``_neuron_index`` symbol is resolved and the process is
complete.

Note that we have actually mixed two stages here, the stage of generating a
structured representation of the code using :class:`CodeItem` objects, and the
stage of generating code strings using :meth:`CodeItem.convert_to`. In fact,
the converting of, for example, ``V`` to ``_arr_V[_neuron_index]`` only happens
at the second stage.

:func:`resolve`
---------------

The first stage, acting on the structured representation of nested
:class:`CodeItem` objects is resolved using the function :func:`resolve`. This
calls :meth:`Symbol.resolve` for each of the symbols in turn. The resolution
order is determined by an optimal efficiency algorithm, see the reference
documentation for :func:`resolve` for the full algorithm description.

:meth:`Symbol.resolve` can do an arbitrary transformation of the input
:class:`CodeItem`, but typically it will either do something like::

	load()
	item
	save()
	
Or something like::

	for name in array:
		item
		
See the reference documentation for :meth:`Symbol.resolve`, and the
documentation for the most important symbols:

* :class:`SliceIndex`
* :class:`ArraySymbol`
* :class:`ArrayIndex`
* :class:`NeuronGroupStateVariableSymbol`

:meth:`~CodeItem.convert_to`
----------------------------

This step is relatively straightforward, each :class:`CodeItem` object has its
``convert_to`` method called iteratively. The important one is in
:class:`MathematicalStatement`, where the left hand side usage is replaced by
:meth:`Symbol.write` and the right hand side usage is replaced by
:meth:`Symbol.read`. In addition, at this stage the syntax of mathematical
statements is corrected, e.g. Python's ``x**y`` is replaced by C++'s
``pow(x,y)`` using ``sympy``.

Code generation in Brian
========================

The four objects used for code generation in Brian are:

:class:`CodeGenStateUpdater`
	Used for numerical integration, see above and reference documentation.
:class:`CodeGenThreshold`
	Used for computing a threshold function.
:class:`CodeGenReset`
	Used for computing post-spike reset.
:class:`CodeGenConnection`
	Used for synaptic propagation.

Numerical integration
---------------------

An integration scheme is generated from an :class:`Equations` object using the
:func:`make_integration_step` function. See reference documentation for that
function for details.

This is carried out by :class:`CodeGenStateUpdater`, which can be used as a
Brian :class:`brian.StateUpdater` object in :class:`brian.NeuronGroup`.

As an example, for Euler integration, the differential equations::

	dx/dt = expr
	
are separated by :class:`Equations` into variable ``x`` with expression
``expr``. This then becomes::

	_temp_x := expr
	x += _temp_x*dt
	
This can then be resolved by the code generation mechanisms described already. 

Synaptic propagation
--------------------

TODO: synaptic propagation, including docstrings and code comments

NOTE: GPU functionality not included for synaptic propagation yet.

GPU
===

.. warning::
	GPU code is highly transitional, many details may change in the future.

GPU code is handled by five classes:

:class:`GPULanguage` (derived from :class:`CLanguage`)
	Identifies the language as CUDA, and stores a singleton
	:class:`GPUManager` object which is used to manage the GPU.
:class:`GPUCode` (derived from :class:`Code`)
	Returned from the code generation process, but mostly just acts as a proxy
	to :class:`GPUManager`.
:class:`GPUKernel`
	Handles the final stage of taking a partially generated kernel (without the
	vectorisation over threads) and computing the final kernel (using
	vectorisation over threads). Also adds data to the
	:class:`GPUSymbolMemoryManager`.
:class:`GPUManager`
	Manages the GPU generally. Stores a set of kernels (:class:`GPUKernel`) and
	manages memory via :class:`GPUSymbolMemoryManager`. Handles joining the
	memory management code and kernel code into a single source file, and
	compiling it.
:class:`GPUSymbolMemoryManager`
	Handles allocation of GPU memory for symbols.

For more details, see the reference documentation for the classes in the order
above.

Note that :class:`CodeGenConnection` is the only code generation version of a
Brian class which is not GPU enabled at present.

Extending code generation
=========================

To extend code generation, you will probably need to add new :class:`Symbol`
classes. Read the documentation for this class to start, and the documentation
for the most important symbols:

* :class:`SliceIndex`
* :class:`ArraySymbol`
* :class:`ArrayIndex`
* :class:`NeuronGroupStateVariableSymbol`

See also :class:`CodeItem`, particularly the process described in
:meth:`CodeItem.generate`.

Inheritance diagrams
====================

The overall structure of the classes in the code generation package are
included below, for reference.

Languages
---------

.. inheritance-diagram::
	brian.experimental.codegen2.languages
	GPULanguage
	:parts: 2

Code objects
------------	

.. inheritance-diagram::
	brian.experimental.codegen2.codeobject
	GPUCode
	:parts: 2

Code items
----------

.. inheritance-diagram::
	brian.experimental.codegen2.codeitems
	brian.experimental.codegen2.statements
	brian.experimental.codegen2.blocks
	:parts: 2
	
Equations
---------

.. inheritance-diagram::
	brian.experimental.codegen2.equations
	brian.experimental.codegen2.expressions
	:parts: 2

Symbols
-------

.. inheritance-diagram::
	brian.experimental.codegen2.symbols
	:parts: 2

Resolution and code output
--------------------------

.. inheritance-diagram::
	brian.experimental.codegen2.dependencies
	brian.experimental.codegen2.formatting
	:parts: 2

Integration
-----------

.. inheritance-diagram::
	brian.experimental.codegen2.integration
	:parts: 2

GPU
---

.. inheritance-diagram::
	brian.experimental.codegen2.gpu.management
	:parts: 2

Brian objects
-------------

Connection
~~~~~~~~~~

.. inheritance-diagram::
	brian.experimental.codegen2.connection
	:parts: 3

Reset
~~~~~

.. inheritance-diagram::
	brian.experimental.codegen2.reset
	:parts: 3

State updater
~~~~~~~~~~~~~

.. inheritance-diagram::
	brian.experimental.codegen2.stateupdater
	:parts: 3

Threshold
~~~~~~~~~

.. inheritance-diagram::
	brian.experimental.codegen2.threshold
	:parts: 3

Reference
=========

blocks
------

.. autoclass:: Block
	:members:
	:undoc-members:
.. autoclass:: ControlBlock
	:members:
	:undoc-members:
.. autoclass:: ForBlock
	:members:
	:undoc-members:
.. autoclass:: PythonForBlock
	:members:
	:undoc-members:
.. autoclass:: CForBlock
	:members:
	:undoc-members:
.. autoclass:: IfBlock
	:members:
	:undoc-members:
.. autoclass:: PythonIfBlock
	:members:
	:undoc-members:
.. autoclass:: CIfBlock
	:members:
	:undoc-members:

codeitems
---------

.. autoclass:: CodeItem
	:members:
	:undoc-members:

codeobject
----------

.. autoclass:: Code
	:members:
	:undoc-members:
.. autoclass:: PythonCode
	:members:
	:undoc-members:
.. autoclass:: CCode
	:members:
	:undoc-members:

connection
----------

.. autoclass:: CodeGenConnection
	:members:
	:undoc-members:
.. autoclass:: DenseMatrixSymbols
	:members:
	:undoc-members:
.. autoclass:: SparseMatrixSymbols
	:members:
	:undoc-members:

dependencies
------------

.. autoclass:: Dependency
	:members:
	:undoc-members:
.. autoclass:: Read
	:members:
	:undoc-members:
.. autoclass:: Write
	:members:
	:undoc-members:
.. autofunction:: get_read_or_write_dependencies

equations
---------

.. autofunction:: freeze_with_equations
.. autofunction:: frozen_equations

expressions
-----------

.. autoclass:: Expression
	:members:
	:undoc-members:

formatting
----------

.. autofunction:: word_substitute
.. autofunction:: flattened_docstring
.. autofunction:: indent_string
.. autofunction:: get_identifiers
.. autofunction:: strip_empty_lines

gpu
---

.. warning::
	GPU code is highly transitional, many details may change in the future.

.. autoclass:: GPUKernel
	:members:
	:undoc-members:
.. autoclass:: GPUManager
	:members:
	:undoc-members:
.. autoclass:: GPUSymbolMemoryManager
	:members:
	:undoc-members:
.. autoclass:: GPUCode
	:members:
	:undoc-members:
.. autoclass:: GPULanguage
	:members:
	:undoc-members:

integration
-----------

.. autoclass:: EquationsContainer
	:members:
	:undoc-members:
.. autofunction:: make_integration_step
.. autofunction:: euler
.. autofunction:: rk2
.. autofunction:: exp_euler

languages
---------

.. autoclass:: Language
	:members:
	:undoc-members:
.. autoclass:: PythonLanguage
	:members:
	:undoc-members:
.. autoclass:: CLanguage
	:members:
	:undoc-members:

makeintegrator
--------------

.. autofunction:: make_c_integrator

reset
-----

.. autoclass:: CodeGenReset
	:members:
	:undoc-members:

resolution
----------

.. autofunction:: resolve

statements
----------

.. autoclass:: Statement
	:members:
	:undoc-members:
.. autoclass:: CodeStatement
	:members:
	:undoc-members:
.. autoclass:: CDefineFromArray
	:members:
	:undoc-members:
.. autoclass:: MathematicalStatement
	:members:
	:undoc-members:
.. autofunction:: statements_from_codestring
.. autofunction:: c_data_type

stateupdater
------------

.. autoclass:: CodeGenStateUpdater
	:members:
	:undoc-members:

symbols
-------

.. autoclass:: Symbol
	:members:
	:undoc-members:
.. autoclass:: RuntimeSymbol
	:members:
	:undoc-members:
.. autoclass:: ArraySymbol
	:members:
	:undoc-members:
.. autoclass:: NeuronGroupStateVariableSymbol
	:members:
	:undoc-members:
.. autoclass:: SliceIndex
	:members:
	:undoc-members:
.. autoclass:: ArrayIndex
	:members:
	:undoc-members:
.. autofunction:: language_invariant_symbol_method
.. autofunction:: get_neuron_group_symbols 

threshold
---------

.. autoclass:: CodeGenThreshold
	:members:
	:undoc-members:
