Growth medium optimization
With mixed-integer linear programming, it is possible to find the "simplest" substrate medium that can support the growth of the model. This medium-optimization analysis minimizes the total amount of exchange reactions that are active (i.e., the flux is not zero). Medium optimization can be used to scan for diverse feeds that can support your model, and to identify metabolites that are crucial for growth.
Here, we will use the toy E. coli toy model and see what are the diverse "minimal" nutrient combinations that can support its growth.
Medium optimization identifies a set of exchange reaction of minimal size, i.e., the smallest possible count of exchange reactions active in the uptake direction. This does not correspond to the smallest or most efficient exchange flux that is achievable – to identify minimal fluxes, use a parsimonious analysis.
using COBREXA
download_model(
"http://bigg.ucsd.edu/static/models/e_coli_core.json",
"e_coli_core.json",
"7bedec10576cfe935b19218dc881f3fb14f890a1871448fc19a9b4ee15b448d8",
)
import JSONFBCModels, HiGHS
model = load_model("e_coli_core.json")
JSONFBCModels.JSONFBCModel(#= 95 reactions, 72 metabolites =#)
To get a good guess on how much the model can grow on an arbitrary medium, we solve it using FBA:
fba_result = flux_balance_analysis(model; optimizer = HiGHS.Optimizer);
fba_result.objective
0.8739215069684305
Find a minimal set of exchanges
Medium optimization is implemented in medium_optimization_analysis
.
The function assumes the negative flux through exchanges is the metabolite uptake direction (which is a common assumption in models). The optimization minimizes the total count of the exchange reactions that are running in the uptake direction.
Accordingly, the function needs to know a way to identify the exchange reactions; by default it uses flux_balance_constraints
with parameter interface = :identifier_prefixes
, and finds the exchanges from the generated interface. If one aims to optimize a different set of exchanges, it is possible to specify these via argument exchange_reactions
.
x = medium_optimization_analysis(
model,
fba_result.objective * 0.9,
optimizer = HiGHS.Optimizer,
)
ConstraintTrees.Tree{Float64} with 5 elements:
:exchange_flag_bounds => ConstraintTrees.Tree{Float64}(#= 20 elements =#)
:medium_cost => 4.0
:medium_flags => ConstraintTrees.Tree{Float64}(#= 20 elements =#)
:medium_size => 4.0
:system => ConstraintTrees.Tree{Float64}(#= 5 elements =#)
We can find the medium from the "flag" variables:
medium = [k for (k, v) in x.medium_flags if v != 0]
4-element Vector{Symbol}:
:EX_glc__D_e
:EX_nh4_e
:EX_o2_e
:EX_pi_e
If one wishes to find a different medium, it is possible to supply "known" flags; in turn, this combination will be avoided.
y = medium_optimization_analysis(
model,
fba_result.objective * 0.9,
optimizer = HiGHS.Optimizer,
known_flags = [x.medium_flags],
);
println(y)
nothing
Turns out that for the toy model, there is no other feasible medium that could support the growth rate! To compensate, we may relax our requirements on the model a little and see if it can grow with a different configuration:
y = medium_optimization_analysis(
model,
fba_result.objective * 0.2,
optimizer = HiGHS.Optimizer,
known_flags = [x.medium_flags],
)
ConstraintTrees.Tree{Float64} with 6 elements:
:exchange_flag_bounds => ConstraintTrees.Tree{Float64}(#= 20 elements =#)
:known_medium_1 => 1.0
:medium_cost => 3.0
:medium_flags => ConstraintTrees.Tree{Float64}(#= 20 elements =#)
:medium_size => 3.0
:system => ConstraintTrees.Tree{Float64}(#= 5 elements =#)
Indeed, we got another medium; showing that the model may exhibit some growth even without oxygen:
medium2 = [k for (k, v) in y.medium_flags if v != 0]
3-element Vector{Symbol}:
:EX_glc__D_e
:EX_nh4_e
:EX_pi_e
This page was generated using Literate.jl.