Compatibility and reproducibility
=================================


.. _supported_python:

Supported Python and numpy versions
-----------------------------------

We follow the approach outlined in numpy's
`deprecation policy <https://numpy.org/neps/nep-0029-deprecation_policy.html>`_. This means that Brian supports:

* All minor versions of Python released 42 months prior to Brian, and at minimum the two latest minor versions.
* All minor versions of numpy released in the 24 months prior to Brian, and at minimum the last three minor versions.

Note that we do not have control about the versions that are supported by the `conda-forge <https://conda-forge.org/>`_
infrastructure. Therefore, ``brian2`` conda packages might not be provided for all of the supported versions. In this
case, affected users can chose to either update the Python/numpy version in their conda environment to a version with a
conda package or to install ``brian2`` via pip.

General policy
--------------

We try to keep backwards-incompatible changes to a minimum. In general, ``brian2`` scripts should continue to work with
newer versions and should give the same results.

As an exception to the above rule, we will always correct clearly identified bugs that lead to incorrect simulation
results (i.e., not just an matter of interpretation). Since we do not want to require new users to take any action
to get correct results, we will change the default behaviour in such cases. If possible, we will give the user an
option to restore the old, incorrect behaviour to reproduce the previous results with newer Brian versions. This would
typically be a preference in the `legacy` category, see `legacy.refractory_timing` for an example.

.. note::
    The order of terms when evaluating equations is not fixed and can change with the version of ``sympy``, the symbolic
    mathematics library used in Brian. Similarly, Brian performs a number of optimizations by default and asks the
    compiler to perform further ones which might introduce subtle changes depending on the compiler and its version.
    Finally, code generation can lead to either Python or C++ code (with a single or multiple threads) executing the
    actual simulation which again may affect the numerical results. Therefore, we cannot guarantee exact, "bitwise"
    reproducibility of results.


Syntax deprecations
-------------------

We sometimes realize that the names of arguments or other syntax elements are confusing and therefore decide to change
them. In such cases, we start to use the new syntax everywhere in the documentation and examples, but leave the former
syntax available for compatiblity with previously written code. For example, earlier versions of Brian used
``method='linear'`` to describe the exact solution of differential equations via sympy (that most importantly applies
to "linear" equations, i.e. linear differential equations with constant coefficients). However, some users interpreted
``method='linear'`` as a "linear approximation" like the forward Euler method. In newer versions of Brian the
recommended syntax is therefore to use ``method='exact'``, but the old syntax remains valid.

If the changed syntax is very prominent,  its continued use in Brian scripts (published by others) could be confusing to
new users. In these cases, we might decide to give a warning when the deprecated syntax is used (e.g. for the ``pre``
and ``post`` arguments in `Synapses` which have been replaced by ``on_pre`` and ``on_post``). Such warnings will contain
all the information necessary to rewrite the code so that the warning is no longer raised (in line with our general
:ref:`policy for warnings <log_level_recommendations>`).

Random numbers
--------------
Streams of random numbers in Brian simulations (including the generation of synapses, etc.) are reproducible when a
seed is set via Brian's `seed` function. Note that there is a difference with regard to random numbers between
:doc:`runtime and standalone mode <../user/computation>`: in runtime mode, numpy's random number generator is always
used – even from generated Cython code. Therefore, the call to `seed` will set numpy's random number generator seed
which then applies to all random numbers. Regardless of whether initial values of a variable are set via an explicit
call to `numpy.random.randn`, or via a Brian expression such as ``'randn()'``, both are affected by this seed. In
contrast, random numbers in standalone simulations will be generated by an independent random number generator (but
based on the same algorithm as numpy's) and the call to `seed` will only affect these numbers, not numbers resulting
from explicit calls to `numpy.random`. To make standalone scripts mixing both sources of randomness reproducible, either
set numpy's random generator seed manually in addition to calling `seed`, or reformulate the model to use code
generation everywhere (e.g. replace ``group.v = -70*mV + 10*mV*np.random.randn(len(group))`` by
``group.v = '-70*mv + 10*mV*randn()'``).

Changing the code generation target can imply a change in the order in which random numbers are drawn from the
reproducible random number stream. In general, we therefore only guarantee the use of the same numbers if the code
generation target and the number of threads (for C++ standalone simulations) is the same.

.. note::
    If there are several sources of randomness (e.g. multiple `PoissonGroup` objects) in a simulation, then the order
    in which these elements are executed matters. The order of execution is deterministic, but if it is not
    unambiguously determined by the ``when`` and ``order`` attributes (see :ref:`scheduling` for details), then it will
    depend on the names of objects. When not explicitly given via the ``name`` argument during the object's creation,
    names are automatically generated by Brian as e.g. ``poissongroup``, ``poissongroup_1``, etc. When you repeatedly
    run simulations within the same process, these names might change and therefore the order in which the elements are
    simulated. Random numbers will then be differently distributed to the objects. To avoid this and get reproducible
    random number streams you can either fix the order of elements by specifying the ``order`` or ``name`` argument,
    or make sure that each simulation gets run in a fresh Python process.

Python errors
-------------

While we try to guarantee the reproducibility of simulations (within the limits stated above), we do so only for code
that does not raise any error. We constantly try to improve the error handling in Brian, and these improvements can
lead to errors raised at a different time (e.g. when creating an object as opposed to when running the simulation),
different types of errors being raised (e.g. `DimensionMismatchError` instead of `TypeError`), or simply a different
error message text. Therefore, Brian scripts should never use ``try``/``except`` blocks to implement program logic.
