Compatibility and reproducibility ================================= .. _supported_python: Supported Python and numpy versions ----------------------------------- We follow the approach outlined in numpy's `deprecation policy `_. 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 `_ 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 `). 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.