GECKO
GECKO algorithm can be used to easily adjust the metabolic activity within the cell to respect many known parameters, measured by proteomics and other methods.
The original description from GECKO is by: Sánchez, et. al., "Improving the phenotype predictions of a yeast genome‐scale metabolic model by incorporating enzymatic constraints.", Molecular systems biology, 2017.
The analysis method and implementation in COBREXA is similar to sMOMENT, but GECKO is able to process and represent much larger scale of the constraints – mainly, it supports multiple isozymes for each reaction, and the isozymes can be grouped into "enzyme mass groups" to simplify interpretation of data from proteomics.
For demonstration, we will generate artificial random data in a way similar to the sMOMENT example:
!isfile("e_coli_core.json") &&
download("http://bigg.ucsd.edu/static/models/e_coli_core.json", "e_coli_core.json")
using COBREXA, GLPK
model = load_model("e_coli_core.json")
import Random
Random.seed!(1) # repeatability
gene_product_masses = Dict(genes(model) .=> randn(n_genes(model)) .* 10 .+ 60)
rxns = filter(
x ->
!looks_like_biomass_reaction(x) &&
!looks_like_exchange_reaction(x) &&
!isnothing(reaction_gene_association(model, x)),
reactions(model),
)
69-element Vector{String}: "PFK" "PFL" "PGI" "PGK" "PGL" "ACALD" "AKGt2r" "PGM" "PIt2r" "ALCD2x" ⋮ "MALt2_2" "MDH" "ME1" "ME2" "NADH16" "NADTRHD" "NH4t" "O2t" "PDH"
The main difference from sMOMENT comes from allowing multiple isozymes per reaction (reactions with missing isozyme informations will be ignored, leaving them as-is):
rxn_isozymes = Dict(
rxn => [
Isozyme(
Dict(isozyme_genes .=> 1),
randn() * 100 + 600, #forward kcat
randn() * 100 + 500, #reverse kcat
) for isozyme_genes in reaction_gene_association(model, rxn)
] for rxn in rxns
)
Dict{String, Vector{Isozyme}} with 69 entries: "ACALD" => [Isozyme(Dict("b0351"=>1), 462.073, 330.922), Isozyme(Dict("b124… "PTAr" => [Isozyme(Dict("b2297"=>1), 471.533, 631.132), Isozyme(Dict("b245… "ALCD2x" => [Isozyme(Dict("b0356"=>1), 617.367, 537.297), Isozyme(Dict("b124… "PDH" => [Isozyme(Dict("b0114"=>1, "b0115"=>1, "b0116"=>1), 670.35, 423.7… "PYK" => [Isozyme(Dict("b1676"=>1), 607.735, 454.975), Isozyme(Dict("b185… "CO2t" => [Isozyme(Dict("s0001"=>1), 847.517, 338.625)] "MALt2_2" => [Isozyme(Dict("b3528"=>1), 383.908, 397.408)] "CS" => [Isozyme(Dict("b0720"=>1), 750.383, 549.73)] "PGM" => [Isozyme(Dict("b0755"=>1), 586.875, 439.579), Isozyme(Dict("b361… "TKT1" => [Isozyme(Dict("b2465"=>1), 650.793, 528.251), Isozyme(Dict("b293… "ACONTa" => [Isozyme(Dict("b0118"=>1), 621.458, 449.334), Isozyme(Dict("b127… "GLNS" => [Isozyme(Dict("b1297"=>1), 411.219, 513.669), Isozyme(Dict("b387… "ICL" => [Isozyme(Dict("b4015"=>1), 657.74, 627.894)] "FBA" => [Isozyme(Dict("b1773"=>1), 575.634, 511.58), Isozyme(Dict("b2097… "FORt2" => [Isozyme(Dict("b0904"=>1), 669.647, 627.895), Isozyme(Dict("b249… "G6PDH2r" => [Isozyme(Dict("b1852"=>1), 737.135, 408.509)] "AKGDH" => [Isozyme(Dict("b0727"=>1, "b0116"=>1, "b0726"=>1), 537.049, 670.… "TKT2" => [Isozyme(Dict("b2465"=>1), 676.15, 452.057), Isozyme(Dict("b2935… "FRD7" => [Isozyme(Dict("b4151"=>1, "b4153"=>1, "b4154"=>1, "b4152"=>1), 5… ⋮ => ⋮
We also construct similar bounds for total gene product amounts:
gene_product_bounds = Dict(genes(model) .=> Ref((0.0, 10.0)))
Dict{String, Tuple{Float64, Float64}} with 137 entries: "b4301" => (0.0, 10.0) "b1602" => (0.0, 10.0) "b4154" => (0.0, 10.0) "b3236" => (0.0, 10.0) "b1621" => (0.0, 10.0) "b1779" => (0.0, 10.0) "b3951" => (0.0, 10.0) "b1676" => (0.0, 10.0) "b3114" => (0.0, 10.0) "b1241" => (0.0, 10.0) "b2276" => (0.0, 10.0) "b1761" => (0.0, 10.0) "b3925" => (0.0, 10.0) "b3493" => (0.0, 10.0) "b3733" => (0.0, 10.0) "b2926" => (0.0, 10.0) "b0979" => (0.0, 10.0) "b4015" => (0.0, 10.0) "b2296" => (0.0, 10.0) ⋮ => ⋮
With this, the construction of the model constrained by all enzymatic information is straightforward:
gecko_model =
model |> with_gecko(;
reaction_isozymes = rxn_isozymes,
gene_product_bounds,
gene_product_molar_mass = gene_product_masses,
gene_product_mass_group = _ -> "uncategorized", # all products belong to the same "uncategorized" category
gene_product_mass_group_bound = _ -> 100.0, # the total limit of mass in the single category
)
Metabolic model of type GeckoModel sparse([9, 51, 55, 64, 65, 73, 9, 51, 55, 64 … 200, 201, 202, 203, 204, 205, 206, 207, 208, 209], [1, 1, 1, 1, 1, 1, 2, 2, 2, 2 … 325, 326, 327, 328, 329, 330, 331, 332, 333, 334], [1.0, 1.0, -1.0, -1.0, 1.0, -0.001546133025849219, 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], 209, 334) Number of reactions: 334 Number of metabolites: 209
(Alternatively, you may use make_gecko_model
, which does the same without piping by |>
.)
The stoichiometry and coupling in the gecko model is noticeably more complex; you may notice new "reactions" added that simulate the gene product utilization:
[stoichiometry(gecko_model); coupling(gecko_model)]
262×334 SparseArrays.SparseMatrixCSC{Float64, Int64} with 1386 stored entries: ⠄⠦⠤⠤⠄⠶⠶⠦⠇⠶⡴⠠⠀⠀⢧⠀⠢⣲⠼⠿⠶⣶⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⡎⢶⡉⣁⡄⣬⣠⡅⠔⠰⡈⠀⠀⠐⡄⡀⠄⣫⡉⡉⠩⠝⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠭⠰⠀⠒⣆⣀⣊⣬⢗⣾⢠⣵⣆⠆⠀⡁⠨⠁⠈⠀⠂⠥⠁⠅⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⣧⡣⠁⠤⠟⠒⠻⠿⡗⠆⠠⣧⣤⢵⠀⢠⣄⡀⠘⠋⠚⠠⠂⠆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⢶⣂⠀⠀⠀⠀⠀⠀⠃⠀⠀⠉⠉⠉⠁⠈⠉⠉⠓⠀⠀⠀⠀⠰⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠈⠙⠺⠤⣀⡀⠀⠤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠤⠀⠠⠄⠀⠱⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠘⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠁⠀⠀⠀⠑⢆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠉⠳⢄⡀⠀⠀⠀⠀⠀⠐⠀⠀⠀⠐⠀⠀⠀⠀⠀⠀⠀⠑⢆⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠲⠄⠀⠠⣄⠀⠀⠀⠀⠀⠐⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠧⣜⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀ ⠒⠰⠤⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙ ⠀⠀⠀⠀⠀⠉⠁⠲⠤⢤⡀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠀⠀⠐⠲⠤⢠⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠘⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒
Again, the resulting model can be used in any type of analysis. For example, flux balance analysis:
opt_model = flux_balance_analysis(gecko_model, GLPK.Optimizer)
A JuMP Model Maximization problem with: Variables: 334 Objective function type: JuMP.AffExpr `JuMP.AffExpr`-in-`MathOptInterface.EqualTo{Float64}`: 209 constraints `JuMP.AffExpr`-in-`MathOptInterface.LessThan{Float64}`: 774 constraints Model mode: AUTOMATIC CachingOptimizer state: ATTACHED_OPTIMIZER Solver name: GLPK Names registered in the model: c_lbs, c_ubs, lbs, mb, ubs, x
Get the fluxes
flux_sol = flux_dict(gecko_model, opt_model)
Dict{String, Float64} with 95 entries: "ACALD" => 0.0 "PTAr" => 6.86778 "ALCD2x" => 0.0 "PDH" => 10.5594 "PYK" => 2.58923 "CO2t" => -15.7718 "EX_nh4_e" => -3.90714 "MALt2_2" => 0.0 "CS" => 1.0062 "PGM" => -15.0145 "TKT1" => 1.8806 "EX_mal__L_e" => 0.0 "ACONTa" => 1.0062 "EX_pi_e" => -2.63593 "GLNS" => 0.183219 "ICL" => 0.0 "EX_o2_e" => -14.9434 "FBA" => 7.27848 "EX_gln__L_e" => 0.0 ⋮ => ⋮
Get the gene product concentrations
gp_concs = gene_product_dict(gecko_model, opt_model)
Dict{String, Float64} with 137 entries: "b4301" => 0.0 "b1602" => 0.0 "b4154" => 0.0 "b3236" => 0.000313395 "b1621" => 0.0 "b3951" => -7.43092e-19 "b1779" => 0.025239 "b1676" => -2.92291e-18 "b3114" => 0.0 "b1241" => 0.0 "b2276" => 0.0488441 "b1761" => 0.00556268 "b3925" => 0.0 "b3493" => 0.00343844 "b3733" => 0.0534869 "b2926" => 0.0332595 "b0979" => 0.0 "b4015" => 0.0 "b2296" => 0.0 ⋮ => ⋮
Get the total masses assigned to each mass group
gene_product_mass_group_dict(gecko_model, opt_model)
Dict{String, Float64} with 1 entry: "uncategorized" => 100.0
Variability:
flux_variability_analysis(gecko_model, GLPK.Optimizer, bounds = gamma_bounds(0.95))
95×2 Matrix{Float64}: 6.05514 11.1416 0.0 14.0378 0.370962 9.86045 -18.1905 -14.2797 1.61973e-15 9.48949 -3.37813 0.0 -1.16712 0.0 -17.1721 -13.2614 2.50413 2.63593 -3.37813 0.0 ⋮ 0.0 0.0 -3.14082 7.2849 0.0 2.97194 0.0 4.02313 26.5111 30.5831 0.0 7.30744 3.71178 4.72073 13.7438 16.3803 0.0 15.4793
...and sampling:
affine_hit_and_run(gecko_model, warmup_from_variability(gecko_model, GLPK.Optimizer))' *
reaction_flux(gecko_model)
3340×95 Matrix{Float64}: 5.69741 7.14345 4.35447 -9.7528 … 0.179103 4.165 1.85149 6.17793 7.68231 4.74709 -9.98991 0.161595 3.95987 1.48158 6.54096 8.12222 4.88527 -10.4291 0.130186 4.47108 1.55276 5.79829 7.23795 4.41735 -9.92733 0.179442 4.31659 1.8863 5.70484 7.46217 4.27064 -9.51379 0.118997 4.03253 1.63139 5.7485 7.1552 4.41012 -9.83999 … 0.187914 4.22067 1.89546 5.72688 7.18375 4.36675 -9.76999 0.174085 4.17519 1.83555 5.52281 6.53495 4.13366 -9.68977 0.251199 4.17018 2.46207 5.71168 7.20184 4.38244 -9.73635 0.172702 4.04388 1.8206 5.6307 7.0402 4.34134 -9.58676 0.154022 3.96686 1.89444 ⋮ ⋱ 5.7591 7.3333 4.18016 -9.80307 0.0971256 3.92446 2.01831 6.11556 7.58397 4.89516 -10.3363 0.186786 3.83085 1.8704 6.62928 7.69967 5.02136 -10.9306 0.178771 4.37013 2.54907 6.0239 7.6031 4.70331 -10.3331 0.172041 4.01933 2.01269 5.99965 7.59361 4.683 -10.3152 … 0.171666 4.04386 2.01911 5.92698 7.97307 4.70791 -10.2445 0.199597 4.04475 1.55914 5.97615 8.07602 4.67444 -10.3848 0.154037 4.17861 1.77528 6.13383 7.96383 5.18158 -10.8211 0.175543 3.28189 1.88694 5.65149 7.75653 4.44149 -9.97254 0.150216 4.02244 1.70548
This page was generated using Literate.jl.