Common Information for JiTCODE, JiTCDDE, and JiTCSDE¶
The following is some detailed information on aspects common to JiTCODE, JiTCDDE, and JiTCSDE.
In the following, JiTC*DE refers to any of the aforementioned modules.
Installation¶
Unix (Linux, MacOS, …)¶
Usually, you will already have a C compiler installed and need not worry about this step. Otherwise, it should be easy to install GCC or Clang through your package manager. Note that for using Clang, it may be necessary to change the
CCflag (see below).Python should be installed by default as well.
The easiest way to install JiTC*DE is via PyPi like this:
pip3 install jitcode --user
Replace
jitcodewithjitcddeorjitcsdeif that’s what you want. Replacepip3withpipif you are working in an environment.
Windows (Anaconda)¶
Install Anaconda.
Install a C compiler. The only one that Setuptools uses without a major struggle is from Microsoft Build Tools for Visual Studio For more details and everything else, see this site.
Open the Anaconda Prompt and run:
pip install jitcode --user
Replace
jitcodewithjitcddeorjitcsdeif that’s what you want.
Building from source¶
Usually you do not need to do this, but it may be the only way if prepackaged SymEngine doesn’t work on your system.
Install SymEngine from source following the instructions here.
Install the SymEngine Python bindings from source following the instructions here.
Install readily available required Python packages, namely Jinja 2, NumPy, SciPy, and Setuptools.
Install JiTC*DE Common and the desired packages from GitHub. The easiest way to do this is probably:
pip3 install git+git://github.com/neurophysik/jitcode
Replace
jitcodewithjitcxde_common,jitcdde, orjitcsdeaccordingly.
Here is a summary of commands for Ubuntu (that should be easily adaptable to most other Unixes):
sudo apt install cmake cython git libgmp-dev python3-jinja2 python3-numpy python3-scipy python3-setuptools
git clone https://github.com/symengine/symengine
cd symengine
cmake .
make
sudo make install
pip3 install \
git+git://github.com/symengine/symengine.py \
git+git://github.com/neurophysik/jitcxde_common \
git+git://github.com/neurophysik/jitcode \
git+git://github.com/neurophysik/jitcdde \
git+git://github.com/neurophysik/jitcsde \
--no-dependencies --user
Networks or other very large differential equations¶
JiTC*DE is specifically designed to be able to handle large differential equations, as they arise, e.g., in networks. There is an explicit example of a network in JiTCODE’s documentation, which is straightforward to translate to JiTCDDE and JiTCSDE.
Chunking¶
JiTC*DE structures large source code into chunks, the size of which can be controlled by the option chunk_size, which is available for all code-generation subroutines.
This has two reasons or uses:
- If JiTC*DE handled the code for very large differential equations naïvely, a problem would arise from the compiler trying to handle megabytes of unstructured code at once, which may use too much time and memory. For some compilers, disabling all optimisation can avert this problem, but then, compiler optimisations usually are a good thing. Chunking is a compromise between the two: Optimisation still happens within chunks, but not across chunks. We obtained better performances in these regards with Clang than with GCC.
- It allows a reasonable parallelisation using OpenMP (see the next section). Note that
chunk_sizehere is also used for regular loops and similar.
If there is an obvious grouping of your \(f\), the group size suggests itself for chunk_size.
For example, if you want to simulate the dynamics of three-dimensional oscillators coupled onto a 40×40 lattice and if the differential equations are grouped first by oscillator and then by lattice row, a chunk size of 120 suggests itself.
Also note that simplifications and common-subexpression eliminations may take a considerable amount of time (and can be disabled).
In particular, if you want to calculate the Lyapunov exponents of a larger system, it may be worthwhile to set simplify to False.
OpenMP Support (multi-processing)¶
Code generated by JiTCODE contains OpenMP pragmas that will make the compiler automatically compile the code such that it will be parallelised on a multi-kernel machine – if the right respective compiler and linker flags are used and the respective libraries are installed.
Each compiling command has an argument omp that when set to True will cause the most generic of these flags to be used.
Depending on the compiler, these flags may not work or not be the best choice.
In this case you pass the desired compiler and linker flags as a pair of lists of strings to the omp argument.
For example, for GCC, you might use:
ODE.compile_C( omp=(["-fopenmp"],["-lgomp"]) )
In most cases, the chunk sizes used by OpenMP correspond to the chunk_size argument of the respective code-generating instruction.
Note that parallelisation comes with a considerable overhead. It is therefore only worthwhile if both:
- Your differential equation is huge (ballpark: hundreds of instructions).
- You have fewer problems (realisations) than cores or cannot run several problems in parallel due to memory constraints or similar.
Choosing the Compiler¶
You can find out which compiler is used by explicitly calling I.compile_C(verbose=True), where I is your JiTC*DE object.
Linux (and other Unixes, like MacOS)¶
Setuptools uses your operating system’s CC flag to choose the compiler.
Therefore, this is what you have to change, if you want to change the compiler.
Some common ways to do this are (using clang as an example for the desired compiler):
- Call
export CC=clangin the terminal before running JiTC*DE. Note that you have to do this anew for every instance of the terminal or write it into some configuration file. - Call
os.environ["CC"] = "clang"in Python.
So far, Clang has proven to be better at handling large differential equations.
Choosing the Module Name¶
The only reason why you may want to change the module name is if you want to save the module file for later use (with save_compiled).
To do this, use the modulename argument of the compile_C command.
If this argument is None or empty, the filename will be chosen by JiTC*DE based on previously used filenames or default to jitced.so.
Note that it is not possible to re-use a module name for a given instance of Python (due to the limitations of Python’s import machinery).
Compiler and Linker Arguments¶
All classes have a compile_C command which has extra_compile_args and extra_link_args as an argument.
If those arguments are left None, defaults (listed below) are chosen depending on the compiler that Setuptools actually uses.
You may want to modify the these arguments for two reasons:
- To tweak the compilation process and results.
- To make JiTC*DE run with a compiler that doesn’t recognise the default arguments.
Often, it’s best not to write your own list, but modify the defaults listed below.
These can be imported from jitcxde_common like this:
from jitcxde_common import DEFAULT_COMPILE_ARGS
You can then modify them before usage, e.g., like this:
ODE.compile_C( extra_compile_args = DEFAULT_COMPILE_ARGS + ["--my-flag"] )
This way you get the most of future versions of JiTC*DE.
Note that in either case, these arguments are appended to (and thus override) whatever Setuptools uses as a default.
-
DEFAULT_COMPILE_ARGS= ['-std=c11', '-Ofast', '-g0', '-march=native', '-mtune=native', '-Wno-unknown-pragmas']¶ A list with the default extra compile arguments. Note that without
-Ofast,-ffast-math, or-funsafe-math-optimizations(if supported by your compiler), you may experience a considerable speed loss since SymEngine uses thepowfunction for small integer powers (cf. SymPy Issue 8997).
-
DEFAULT_LINK_ARGS= ['-lm']¶ A list with the default linker arguments.
-
MSVC_COMPILE_ARGS= ['/Ox', '/wd4068', '/wd4146', '/wd4018']¶ A list with the default compile arguments for the Microsoft compiler. I could not find what level of optimisation is needed to address the problem of SymEngine using the
powfunction for small integer powers (SymPy Issue 8997).
-
MSVC_LINK_ARGS= ['/ignore:4197']¶ A list with the default linker arguments for the Microsoft compiler.
SymPy vs SymEngine¶
SymPy’s core is completely written in Python and hence rather slow. Eventually, this core shall be replaced by a faster, compiled one: SymEngine, more specifically the SymEngine Python wrapper. SymEngine is not yet ready for this, but it already has everything needed for JiTC*DE’s purpose, except for some side features like common-subexpression elimination and lambdification (only for JiCDDE). Also SymEngine internally resorts to SymPy for some features like simplification. By using SymEngine instead of SymPy, code generation in JiTC*DE is up to nine hundred times faster.
Pratically, you can use both SymPy and SymEngine to provide the input to JiTC*DE, as they are compatible with each other. However, using SymPy may considerably slow down code generation. Also, some advanced features of SymPy may not translate to SymEngine, but so far the only one I can see making sense in a typical JiTC*DE application are SymPy’s sums and those can be easily replaced by Python sums.
Note that while SymEngine’s Python wrapper is sparsely documented, almost everything that is relevant to JiTC*DE behaves analogously to SymPy and the latter’s documentation serves as a documentation for SymEngine as well. For this reason, JiTC*DE’s documentation also often links to SymPy’s documentation when talking about SymEngine features.
Common Mistakes and Questions¶
- If you want to use mathematical functions like
sin,exporsqrtyou have to use the SymEngine variants. For example, instead ofmath.sinornumpy.sin, you have to usesymengine.sin. - If you wish to use step functions to drive the system or similar, the best alternative is usually to use a sharp sigmoidal instead.
SymEngine has not implemented SymPy’s
Piecewiseyet, but more importantly discontinuities can cause all sorts of problems with the integrators. If your step-wise behaviour depends on time (e.g., an external pulse that is limited in time), you can also integrate up to the point of the step, changefor a control parameter, and continue. Note that for DDEs this may introduce a discontinuity that needs to be dealt with like an initial discontinuity. - If you get unexpected or cryptic errors, please run the respective class’s
checkfunction and also check that all input has the right format and functions have the right signature. - If JiTC*DE’s code generation and compilation is too slow or bursts your memory, check:
- Did you deactivate simplifications and common-subexpression eliminations?
- Did you use a generator?
- Did you use chunking?
- Did you use SymEngine symbols and functions instead of SymPy ones?
- Consider using Clang as a compiler.