Exploring model variants with screen
This notebooks demonstrates a simple use of screen
to explore large number of model variants. On the toy E. Coli model, we try to map the impact of knocking out single reactions and 2-reaction combinations.
First, let's download the data and load the packages and the model
!isfile("e_coli_core.json") &&
download("http://bigg.ucsd.edu/static/models/e_coli_core.json", "e_coli_core.json");
using COBREXA, Tulip
model = load_model(StandardModel, "e_coli_core.json")
Metabolic model of type StandardModel sparse([9, 51, 55, 64, 65, 34, 44, 59, 66, 64 … 20, 22, 23, 25, 16, 17, 34, 44, 57, 59], [1, 1, 1, 1, 1, 2, 2, 2, 2, 3 … 93, 93, 94, 94, 95, 95, 95, 95, 95, 95], [1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0 … 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0], 72, 95) Number of reactions: 95 Number of metabolites: 72
Preparing the functions
While we could use the with_changed_bound
to limit the reaction rates, but we will make a slightly more precise, usage-tailored modification. This is a straightforward modification of the with_changed_bound
that does not set bounds "outside" of the original bounds:
with_limited_rate(reaction_id::String, limit) =
model::StandardModel -> begin
m = copy(model)
m.reactions = copy(model.reactions)
r = m.reactions[reaction_id] = copy(model.reactions[reaction_id])
if -limit > r.lb
r.lb = -limit
end
if limit < r.ub
r.ub = limit
end
m
end
with_limited_rate (generic function with 1 method)
Knocking out single reactions
This can be applied to see how much biomass can the model produce with certain reactions limited to almost zero:
get_biomass(x) = isnothing(x) ? 0 : x["BIOMASS_Ecoli_core_w_GAM"]
productions = screen_variants(
model,
[[with_limited_rate(rxn, 0.1)] for rxn in reactions(model)],
model -> get_biomass(flux_balance_analysis_dict(model, Tulip.Optimizer)),
)
95-element Vector{Real}: 0.7110773172306842 0.8739215069620168 0.8633809523181394 0.007974352115966855 0.8640171040038155 0.8739215063456838 0.87392150695555 0.009054524481803774 0.027183515911490083 0.8739215069217056 ⋮ 0.8739215022678488 0.8270283079105472 0.8739215065011818 0.8739215045533565 0.21346280281143498 0.8739215053667706 0.018339201041498398 0.21526265974680325 0.7975549891965423
This can be nicely plotted to give a more comprehensive overview of which reactions are critical or not:
using CairoMakie
disp_rxns = rand(1:95, 20) # only display 20 random fluxes to save space
barplot(
productions[disp_rxns],
direction = :x,
axis = (
yticks = (1:20, reactions(model)[disp_rxns]),
xlabel = "Flux [mmol/gDW/h]",
ylabel = "Reaction ID",
),
)
Knocking out reaction combinations
It is very easy to prepare matrices of biomass productions from all possible two-reaction knockouts. To make it more interesting, we will restrict one of the reactions of the pair a bit less, to see more possible outcomes.
We do not process all reactions here to make the notebook rendering efficient, but you can easily remove the restriction, and speed the process up using parallel execution, by specifying workers
argument (see documentation of screen
for details)
rxns = reactions(model)
productions = screen_variants(
model,
[
[with_limited_rate(rxn1, 3), with_limited_rate(rxn2, 0.1)] for rxn1 in rxns,
rxn2 in rxns
],
model -> get_biomass(flux_balance_analysis_dict(model, Tulip.Optimizer)),
)
f = Figure()
ax = Axis(
f[1, 1],
ylabel = "Reaction KO",
xlabel = "Reaction KO",
yticks = (1:20, reactions(model)[disp_rxns]),
xticks = (1:20, reactions(model)[disp_rxns]),
xticklabelrotation = -pi / 4,
)
hm = heatmap!(ax, productions[disp_rxns, disp_rxns])
Colorbar(f[1, 2], hm, label = "Flux [mmol/gDW/h]")
f
This page was generated using Literate.jl.