Modeling a metabolic flow network with CVXPY
Every cell has a myriad of metabolic enzymes producing internal building blocks from nutrients found in the environment. These building blocks are then used as monomers for large polymer molecules, and these polymers constitutes most of the mass of our cells. So we have to make a lot of building blocks per second!
Construction of the building blocks themselves is done by a network of thousands of unique metabolic enzymes in our cells.
For this blog post, a good mental model of this network is actually a network of water pipes (reactions) and reservoirs (molecules). A chemical reaction is a pipe between some sets of reservoirs, and the flow is the reaction rate. Enzymes make the pipes bigger, enabling a faster flow than what would be possible normally.
A toy example
Let's say we have a set of reactions with the structure:
Implementing a metabolic flow network in CVXPY
Since solving the flow problem gets complicated for large networks, we can instead solve it as a linear programming problem. CVXPY is a Python library for convex optimization problems that allows us to easily express optimization problems in a mathematically intuitive way.
For a linear program, we typically want to maximize or minimize an objective function. Let's say in this case,
we've measured the maximum rate of reaction
We can implement this in CVXPY in a very similar fashion:
import cvxpy as cp
import numpy as np
# Define the stoichiometric matrix S
S = np.array([
[1, -2, 0, 0],
[0, 1, -1, 0],
[0, 0, 1, -1]
])
# Define the reaction rates vector v as a variable in CVXPY
v = cp.Variable(4)
# Define the steady state constraint S @ v = 0
constraints = [S @ v == 0]
# Append the constraint that the rate of reaction R1 is less than 10
constraints.append(v[0] <= 10)
# Define the objective function
objective = cp.Maximize(v[3])
# Formulate the problem, which has the objective and constraints
problem = cp.Problem(objective, constraints)
# Solve the problem, which returns the optimal vector v
problem.solve()
# Print the solution
print("Optimal v:", v.value)
# v = [10, 5, 5, 5]
The perks of understanding convex optimization theory for modeling metabolic networks
Many research groups use premade packages for solving metabolic flow problems, such as COBRApy or FBApy. These packages are great for quickly getting a solution for simple problem structures, but understanding the underlying theory of convex optimization can be incredibly useful if you want to include more complex constraints or objective functions in your model.As an example, let's say that you've measured the rate of reaction
So you go back and look at your data, and you realize that the rate of reaction
It turns out that fortunately, L2-norms are convex functions, and actually correspond exactly to setting a Gaussian prior for the reaction rates. We can control the mean and variance of the prior by adjusting the L2-norm as follows:
Adding this as a constraint in CVXPY is as simple as:
# Define the prior mean and variance
mu = 5
sigma = 2
# Add the L2-norm penalty to the objective function
objective += (v[1] - mu)**2 / (2 * sigma**2)
This is just an example of how understanding the underlying theory of convex optimization can help you model more complex biological systems. I hope this blog post has been helpful in understanding how to model metabolic networks in just a few lines of code!