Skip to content

Getting started

How to install enzax

pip install enzax

To install the latest version of enzax from GitHub:

$ pip install git+https://github.com/dtu-qmcm/enzax.git@main

Make your own kinetic model

Enzax provides building blocks for you to construct a wide range of differentiable kinetic models using pre-written and tested rate laws.

Here we write a model describing a simple linear pathway with two state variables, two boundary species and three reactions.

First we import some enzax classes, as well as equinox and both JAX and standard versions of numpy:

import equinox as eqx

import numpy as np

from enzax.kinetic_model import RateEquationModel
from enzax.rate_equations import (
    AllostericReversibleMichaelisMenten,
    ReversibleMichaelisMenten,
)

Next we start specifying our model's structure by providing stoichiometric coefficients for its reactions and saying which species represent ODE state variables (aka which ones are "balanced").

stoichiometry = {
    "r1": {"m1e": -1.0, "m1c": 1.0},
    "r2": {"m1c": -1.0, "m2c": 1.0},
    "r3": {"m2c": -1.0, "m2e": 1.0},
}
reactions = ["r1", "r2", "r3"]
species = ["m1e", "m1c", "m2c", "m2e"]
balanced_species = ["m1c", "m2c"]

Next we specify the model's rate equations. Note that the order of the equations should match our reactions list and that the indexes that refer to species, like ix_allosteric_activators and ix_ki_species should match the order of the species list.

rate_equations = [
    AllostericReversibleMichaelisMenten(
        ix_allosteric_activators=np.array([2]), subunits=1
    ),
    AllostericReversibleMichaelisMenten(
        ix_allosteric_inhibitors=np.array([1]), ix_ki_species=np.array([1])
    ),
    ReversibleMichaelisMenten(water_stoichiometry=0.0),
]

Now we can make a RateEquationModel object

model = RateEquationModel(
    stoichiometry=stoichiometry,
    species=species,
    reactions=reactions,
    balanced_species=balanced_species,
    rate_equations=rate_equations,
)

Next we specify a set of kinetic parameters as a dictionary:

from jax import numpy as jnp

parameters = {
    "log_substrate_km": {
        "r1": jnp.array([0.1]),
        "r2": jnp.array([0.5]),
        "r3": jnp.array([-1.0]),
    },
    "log_product_km": {
        "r1": jnp.array([-0.2]),
        "r2": jnp.array([0.0]),
        "r3": jnp.array([0.5]),
    },
    "log_kcat": {"r1": jnp.array(-0.1), "r2": jnp.array(0.0), "r3": jnp.array(0.1)},
    "dgf": jnp.array([-3.0, -1.0]),
    "log_ki": {"r1": jnp.array([]), "r2": jnp.array([1.0]), "r3": jnp.array([])},
    "temperature": jnp.array(310.0),
    "log_enzyme": {
        "r1": jnp.log(jnp.array(0.3)),
        "r2": jnp.log(jnp.array(0.2)),
        "r3": jnp.log(jnp.array(0.1)),
    },
    "log_conc_unbalanced": jnp.log(jnp.array([0.5, 0.1])),
    "log_tc": {"r1": jnp.array(-0.2), "r2": jnp.array(0.3)},
    "log_dc_activator": {"r1": jnp.array([-0.1]), "r2": jnp.array([])},
    "log_dc_inhibitor": {"r1": jnp.array([]), "r2": jnp.array([0.2])},
}

Note that the parameters use jnp whereas the structure uses np. This is because we want JAX to trace the parameters, whereas the structure should be static. Read more about this here.

To test out the model, we can see if it returns some fluxes and state variable rates when provided a set of balanced species concentrations:

conc = jnp.array([0.43658744, 0.12695706])
flux = model.flux(conc, parameters)
flux
dcdt = model.dcdt(conc, parameters)
dcdt

Load a kinetic model from SBML

Enzax supports loading kinetic models from SBML files, either locally:

from pathlib import Path
from enzax.sbml import load_libsbml_model_from_file, sbml_to_enzax

path = Path("path") / "to" / "sbml_file.xml"
libsbml_model = load_libsbml_model_from_file(path)
model = sbml_to_enzax(libsbml_model)

or from a url:

from enzax.sbml import load_libsbml_model_from_url, sbml_to_enzax

url = "https://raw.githubusercontent.com/dtu-qmcm/enzax/refs/heads/main/tests/data/exampleode.xml"
libsbml_model = load_libsbml_model_from_url(url)
model = sbml_to_enzax(libsbml_model)

Note

The parameters in the sbml file have to have unique identifiers. In CopasiUI it is possible to make Global Quantities as assignments and odes. Enzax currently does not support this.

Find a kinetic model's steady state

Enzax provides a few example kinetic models, including methionine, a model of the mammalian methionine cycle.

Here is how to find this model's steady state (and its parameter gradients) using enzax's get_kinetic_model_steady_state function:

from enzax.examples.methionine import model, parameters
from enzax.steady_state import get_steady_state
from jax import numpy as jnp

guess = jnp.full((5,) 0.01)

steady_state = get_steady_state(model, guess, parameters)

To access the Jacobian of this steady state with respect to the model's parameters, we can use JAX's jacrev function:

jacobian = jax.jacrev(get_steady_state, argnums=2)(model, guess, parameters)
jacobian