RateFlowODE combines neural networks with stoichiometric principles to discover reaction mechanisms from experimental time-series data. Unlike traditional Neural ODEs that learn species dynamics directly, RateFlowODE learns individual reaction rates and combines them through a stoichiometric matrix, enabling the discovery of biochemical reaction networks without prior mechanistic knowledge.
Stoichiometric Decomposition of Biochemical Systems
The fundamental innovation of RateFlowODE lies in its decomposition of species dynamics into individual reaction contributions. Rather than learning the net rate of change for each species directly, the approach models the system as:dtdy=S⋅r(y,t,θ)where:
y represents species concentrations
S is the stoichiometric matrix (nspecies×nreactions)
r(y,t,θ) are individual reaction rates predicted by the neural network
This mathematical structure enforces biochemical realism by explicitly representing the relationship between individual reactions and their collective effect on species concentrations.
The neural network component predicts reaction rates based on current species concentrations and time:r(y,t,θ)=ReLU(MLP(y,t,θ))The ReLU activation ensures that reaction rates remain non-negative, consistent with the physical interpretation of reaction rates as positive quantities. The MLP (Multi-Layer Perceptron) learns the complex concentration dependencies that govern reaction kinetics.
Create a RateFlowODE instance for reaction network discovery:
Copy
Ask AI
import catalax as ctximport catalax.neural as ctnimport jax.random as jrandom# Define system structure (species must be known)species_order = ["S", "E", "P", "ES"] # Substrate, Enzyme, Product, Complexobservable_indices = [0, 2] # Only S and P are measurable# Create RateFlowODE with learnable stoichiometrykey = jrandom.PRNGKey(42)rateflow_ode = ctn.RateFlowODE( data_size=len(species_order), # Number of species reaction_size=3, # Number of reactions to discover width_size=64, # Neural network width depth=3, # Neural network depth species_order=species_order, observable_indices=observable_indices, learn_stoich=True, # Enable stoichiometry learning activation=jax.nn.softplus, # Smooth activation for rates key=key)
When reaction mechanisms are partially known, provide the stoichiometric structure:
Copy
Ask AI
import jax.numpy as jnp# Define known reaction mechanism for Michaelis-Menten kinetics# Reactions: E + S ⇌ ES, ES → E + Pstoich_matrix = jnp.array([ [-1, 0], # S: consumed in reaction 1, not involved in reaction 2 [-1, 1], # E: consumed in reaction 1, produced in reaction 2 [ 1, -1], # ES: produced in reaction 1, consumed in reaction 2 [ 0, 1] # P: not involved in reaction 1, produced in reaction 2])# Create RateFlowODE with fixed stoichiometryrateflow_ode_fixed = ctn.RateFlowODE( data_size=4, reaction_size=2, width_size=32, depth=2, species_order=species_order, observable_indices=observable_indices, learn_stoich=False, # Fix stoichiometry stoich_matrix=stoich_matrix, # Provide known structure key=key)
Mass conservation represents a fundamental constraint in biochemical systems where the total amount of certain molecular species remains constant throughout the reaction process. This is particularly important for enzyme systems where the total enzyme concentration should remain unchanged, or in metabolic pathways where specific atomic groups are conserved.The mass constraint is mathematically represented as:M⋅y(t)=cwhere M is the mass constraint matrix, y(t) is the vector of species concentrations, and c is the vector of conserved quantities. For enzyme conservation in Michaelis-Menten kinetics, this ensures that E(t)+ES(t)=Etotal at all times during the reaction.
Copy
Ask AI
# Define conservation constraints# Example: Total enzyme conservation (E + ES = constant)mass_constraint = jnp.array([ [0, 1, 1, 0] # E + ES conservation constraint])# Create RateFlowODE with conservation constraintsrateflow_ode_conserved = ctn.RateFlowODE( data_size=4, reaction_size=3, width_size=64, depth=3, species_order=species_order, observable_indices=observable_indices, learn_stoich=True, mass_constraint=mass_constraint, # Enforce conservation key=key)
Examine how reaction rates depend on species concentrations:
Copy
Ask AI
# Create phase plots showing rate dependenciesrate_grid_fig = trained_rateflow.plot_rate_grid( dataset=experimental_data, model=original_model, rate_indices=[0, 1, 2], # Analyze first three reactions species_pairs=[("S", "E"), ("ES", "P")], # Focus on key species pairs representative_time=0.0, # Time point for analysis grid_resolution=50, # Resolution of concentration grid figsize_per_subplot=(6, 5), range_extension=0.3, # Extend beyond data range show=True)