Skip to main content

jabsim

A jax-based package for simulating ODE models of biological systems, where all variables are non-negative. This enforcement of non-negativity, which neither scipy.solve_ivp nor diffrax solvers can give you, is why you may need jabsim. This is achieved by clamping the state variables to zero whenever they are negative

jabsim is powered by the jax package for high-performance computing and parallelisation. This means that jabsim simulations can be jit-compiled and parallelised on a GPU or TPU using jax.vmap or shard mapping.

How to use jabsim

  1. Make an ODE function which calculates the derivative $\frac{dx}{dt}$ from the arguments t, x, par in this order.
    • What do the arguments stand for?
      • t is the time at this point in the simulation
      • x is the state vector at this time point
      • args is a tuple of extra arguments passed on to the ODE function.
    • IMPORTANT: if you don't know jax, there are a few differences to keep in mind:
      • If you normally use numpy functions in your ODE, import jax.numpy as jnp and use it instead of np. jax with jax.numpy will get automatically installed as a dependency of jabsim when you install it.
      • Be careful using loops and if-statements. If you're an amateur programmer, just avoid doing all that. Otherwise, have a look at the JAX documentation .
  2. Import jabsim and call jabsim.sim to simulate. The arguments are as follows:
    • Required arguments:
      • par: a list, array or dict of model parameters as in your ODE function
      • model_ode: the ODE function you created
      • x0: the initial state vector as a 1D np.array or jnp.array
      • tf: tuple only. The ODE will be simulated for time points between tf[0] and tf[1] (inclusive).
      • savetimestep: interval between the time points at which the trajectory is saved
      • simulator: string specifyingthe simulation method to use
        • "euler": Euler simulator.
        • "rk4": Runge-Kutta 4th order simulator. Slower per ODE integration step but more accurate, hence allowing larger steps for the same accuracy.
      • ode_steps_in_savetimestep: number of ODE integration steps within one timestep
        • e.g. if savetimestep=0.5 hours and ode_step_in_savetimesteps=100, there will be 100 integration steps per 0.5 hour, so the ODE integration step size will be 0.5/100=0.05 hours.
        • high ode_steps_in_savetimestep number increases accuracy but increases runtimes
    • Optional arguments:
      • return_numpy (optional): if True (by default, it is) the output will be in the np.array format, otherwise it will be jnp.array.
      • extinction_thresholds (optional): a 1D np.array or jnp.array of the same shape as your state vector. Some positive number for any state which can go extinct, -1 for any states which cannot go extinct. If state falls below its threshold, it stays at 0 forever. By default, no species can go extinct.
  3. Running jabsim.sim() will return the arrays ts and xs as np.array or jnp.array, as well as a boolean value success.
    • ts: array of timepoints between tf[0] and tf[1] with savetimestep hours, seconds or whatever units you are using between each two consecutive point
    • xs: system trajectory saved as an array at the time points in ts - axis 0 for time, axis 1 for entries in the state vector (i.e. xs.shape[0]=len(ts)).
    • success: boolean value; True if no entry in xs is nan or inf, False otherwise.

Notes

  • In practice, 500 steps per hour (e.g. savetimestep=0.5, ode_steps_in_savetimestep=250 or savetimestep=1.0, ode_steps_in_savetimestep=500) works well for the RK4 solver. For the Euler solver, 1e4 steps per hours is reasonably good.
  • For benchmarking, you can also set simulator="scipy" to simulate your ODE with scipy.solve_ivp (but without any of the delicious jax features of the solvers above). In that case, don't use the arguments ode_steps_in_savetimestep and savetimestep. Instead, you can optionally specify:
    • solver: string describing any solver which may be used with scipy.solve_ivp. By default, we have solver="LSODA".
    • tols: dictionary of relative and absolute tolerances for the scipy solver. By default, tols={'rtol': 1e-6, 'atol': 1e-9}.
    • dt0: starting integration step size. By default, dt0=0.1.
  • If you want to make use of jax parallelisation, make sure to set return_numpy=False so that the solver would operate with jnp.array objects only.
  • On Linux only, jabsim's use of JAX for efficient parallelised computing may conflict with the pyABC package's multicore sampling. To fix this, do the following:
    • When importing packages, add: import multiprocessing as mp; mp.set_start_method('spawn').
    • When initialising the sampler, set pickle=True, e.g. sampler = pyabc.sampler.MulticoreSampler(n_procs=4, pickle=True).

Example

Let us integrate a simple one-dimensional ODE $\frac{dx}{dt} = a x^2$. For the initial condition $x_0=1$ and $a=0.4$, this has the analytical solution $x = \frac{1}{1-0.4t}$. This means we can verify that for savetimestep=0.5, jabsim.sim() produces ts=np.array([0, 0.5, 1.0]) and xs=np.array([1.0, 1.25, 1.66666667]). All entries in xs are finite, hence success=True.

# import jabsim
import jabsim

# import jax.numpy for numpy operations
import jax.numpy as jnp

# our model ODE function returning a list of one element
def model_ode(t, x, args):
    # unpack args - get the dictiory of parameters
    par, = args
    
    # use jnp to square x 
    # (here you could just as well use x[0]**2, we just want to make a point)
    x_squared = jnp.square(x[0])
    
    # return dx/dt as a list - with one entry for a one-dimensional ODE
    return [par['a'] * x_squared]

# our dictionary of paramneters
par = {'a': 0.4}

ts, xs, success = jabsim.sim(
    model_ode=model_ode,
    args=(par,),
    x0=jnp.array([1.0]),
    tf=(0.0, 1.0),
    savetimestep=0.5,
    simulator='rk4',
    ode_steps_in_savetimestep=10,
)

# print the timne
print(ts)
print(xs)
print(success)

Citation

If you find this package useful in your work, please cite the paper below: code for its Showcase 2 served as jabsim's direct ideological precursor.

@article{Gallup2024,
	author = {Gallup, Olivia and Sechkar, Kirill and Towers, Sebastian and Steel, Harrison},
	title = {Computational Synthetic Biology Enabled through JAX: A Showcase},
	journal = {ACS Synth. Biol.},
	volume = {13},
	number = {9},
	pages = {3046},
	year = {2024},
	doi = {10.1021/acssynbio.4c00307}
}

The original JAX package should be cited as:

@software{jax2018github,
  author = {James Bradbury and Roy Frostig and Peter Hawkins and Matthew James Johnson and Yash Katariya and Chris Leary and Dougal Maclaurin and George Necula and Adam Paszke and Jake Vander{P}las and Skye Wanderman-{M}ilne and Qiao Zhang},
  title = {{JAX}: composable transformations of {P}ython+{N}um{P}y programs},
  url = {http://github.com/jax-ml/jax},
  version = {0.3.13},
  year = {2018},
}

Release files for jabsim 0.1.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for jabsim 0.1.5
File Size Uploaded
jabsim-0.1.5.tar.gz 8.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for jabsim 0.1.5
File Interpreter ABI Platform
jabsim-0.1.5-py3-none-any.whl Python 3 none any Details

Total release size: 17.3 kB

Release files / jabsim-0.1.5.tar.gz

Download URL jabsim-0.1.5.tar.gz
Size 8.9 kB
Tags Source
SHA-256 checksum
How to use checksums
bc9b91b413ed772319b43b95bff8955194cfe919e39d57efd55549539656b7b9
BLAKE2b-256 checksum
How to use checksums
a8b1f26403fa6557dde6282426a76b193f9ff70c25f7054793b6a5c5c68d257c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release files / jabsim-0.1.5-py3-none-any.whl

Download URL jabsim-0.1.5-py3-none-any.whl
Size 8.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5a1629f5e51dbc584077fac224c7871be5c04e1937aada557d7a09287bfb9a44
BLAKE2b-256 checksum
How to use checksums
d596bf4f6831b3903624a5fd751ebafc31f9368e103eab65cc9cf0eff3831c00
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.5 This release

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page