Performing ensemble simulations

With a functional Ensemble in hand, you're ready to perform simulations. In medusa, most simulations are performed by setting the model structure to represent an individual member, using cobrapy functions for the actual simulation, then repeating for all or many ensemble members.

Ensemble Flux Balance Analysis

Flux balance analysis (FBA) is one of the most widely used techniques in systems biology. See What is flux balance analysis? for an introduction to FBA, and the cobrapy documentation to see how FBA is performed with a single model.

When using medusa for FBA, the environmental conditions and objective function should be specified in ensemble.base_model, just as if it were a normal cobrapy Model:


In [1]:
import medusa
from medusa.test import create_test_ensemble

ensemble = create_test_ensemble("Staphylococcus aureus")

In [2]:
ensemble.base_model.objective.expression


Out[2]:
1.0*bio1 - 1.0*bio1_reverse_b18f7

The current objective function is the biomass reaction (bio1)--to change this, just set the objective to another reaction. Let's change the objective to CO2 exchange, then change it back to biomass production:


In [3]:
ensemble.base_model.objective = 'EX_cpd00011_e'
print(ensemble.base_model.objective.expression)
ensemble.base_model.objective = 'bio1'
print(ensemble.base_model.objective.expression)


1.0*EX_cpd00011_e - 1.0*EX_cpd00011_e_reverse_896eb
1.0*bio1 - 1.0*bio1_reverse_b18f7

Similarly, you can manipulate the environmental conditions as in cobrapy. The base model for this example ensemble is from ModelSEED, so exchange reactions are specified with the 'EX_' prefix, followed by the metabolite id. Let's take a look at the exchange reactions that are currently open:


In [4]:
medium = ensemble.base_model.medium
medium


Out[4]:
{'EX_cpd00001_e': 1000.0,
 'EX_cpd00007_e': 1000.0,
 'EX_cpd00009_e': 1000.0,
 'EX_cpd00010_e': 1000.0,
 'EX_cpd00011_e': 1000.0,
 'EX_cpd00012_e': 1000.0,
 'EX_cpd00013_e': 1000,
 'EX_cpd00023_e': 1000.0,
 'EX_cpd00024_e': 1000.0,
 'EX_cpd00027_e': 1000.0,
 'EX_cpd00028_e': 1000.0,
 'EX_cpd00029_e': 1000,
 'EX_cpd00030_e': 1000.0,
 'EX_cpd00033_e': 1000.0,
 'EX_cpd00034_e': 1000.0,
 'EX_cpd00035_e': 1000.0,
 'EX_cpd00039_e': 1000.0,
 'EX_cpd00041_e': 1000.0,
 'EX_cpd00047_e': 1000.0,
 'EX_cpd00048_e': 1000.0,
 'EX_cpd00051_e': 1000.0,
 'EX_cpd00053_e': 1000.0,
 'EX_cpd00054_e': 1000.0,
 'EX_cpd00058_e': 1000.0,
 'EX_cpd00060_e': 1000.0,
 'EX_cpd00063_e': 1000.0,
 'EX_cpd00064_e': 1000.0,
 'EX_cpd00066_e': 1000.0,
 'EX_cpd00067_e': 1000.0,
 'EX_cpd00069_e': 1000.0,
 'EX_cpd00072_e': 1000,
 'EX_cpd00073_e': 1000.0,
 'EX_cpd00075_e': 1000.0,
 'EX_cpd00076_e': 1000.0,
 'EX_cpd00079_e': 1000,
 'EX_cpd00080_e': 1000.0,
 'EX_cpd00082_e': 1000.0,
 'EX_cpd00092_e': 1000.0,
 'EX_cpd00094_e': 1000,
 'EX_cpd00098_e': 1000.0,
 'EX_cpd00099_e': 1000.0,
 'EX_cpd00100_e': 1000.0,
 'EX_cpd00104_e': 1000.0,
 'EX_cpd00105_e': 1000.0,
 'EX_cpd00117_e': 1000.0,
 'EX_cpd00119_e': 1000.0,
 'EX_cpd00122_e': 1000.0,
 'EX_cpd00129_e': 1000.0,
 'EX_cpd00130_e': 1000.0,
 'EX_cpd00137_e': 1000.0,
 'EX_cpd00138_e': 1000.0,
 'EX_cpd00141_e': 1000,
 'EX_cpd00142_e': 1000,
 'EX_cpd00149_e': 1000.0,
 'EX_cpd00159_e': 1000.0,
 'EX_cpd00179_e': 1000.0,
 'EX_cpd00182_e': 1000.0,
 'EX_cpd00184_e': 1000.0,
 'EX_cpd00205_e': 1000.0,
 'EX_cpd00208_e': 1000.0,
 'EX_cpd00220_e': 1000.0,
 'EX_cpd00222_e': 1000.0,
 'EX_cpd00232_e': 1000,
 'EX_cpd00244_e': 1000.0,
 'EX_cpd00246_e': 1000.0,
 'EX_cpd00249_e': 1000.0,
 'EX_cpd00254_e': 1000.0,
 'EX_cpd00264_e': 1000.0,
 'EX_cpd00268_e': 1000.0,
 'EX_cpd00276_e': 1000.0,
 'EX_cpd00277_e': 1000.0,
 'EX_cpd00305_e': 1000.0,
 'EX_cpd00309_e': 1000.0,
 'EX_cpd00314_e': 1000.0,
 'EX_cpd00320_e': 1000,
 'EX_cpd00322_e': 1000.0,
 'EX_cpd00355_e': 1000.0,
 'EX_cpd00367_e': 1000.0,
 'EX_cpd00393_e': 1000.0,
 'EX_cpd00396_e': 1000,
 'EX_cpd00412_e': 1000.0,
 'EX_cpd00438_e': 1000.0,
 'EX_cpd00492_e': 1000,
 'EX_cpd00531_e': 1000.0,
 'EX_cpd00540_e': 1000.0,
 'EX_cpd00550_e': 1000.0,
 'EX_cpd00588_e': 1000.0,
 'EX_cpd00637_e': 1000.0,
 'EX_cpd00654_e': 1000.0,
 'EX_cpd00681_e': 1000.0,
 'EX_cpd00709_e': 1000,
 'EX_cpd00794_e': 1000.0,
 'EX_cpd00971_e': 1000.0,
 'EX_cpd01012_e': 1000.0,
 'EX_cpd01030_e': 1000.0,
 'EX_cpd01080_e': 1000.0,
 'EX_cpd01171_e': 1000.0,
 'EX_cpd01262_e': 1000.0,
 'EX_cpd01293_e': 1000,
 'EX_cpd01307_e': 1000,
 'EX_cpd01329_e': 1000.0,
 'EX_cpd01914_e': 1000.0,
 'EX_cpd03279_e': 1000.0,
 'EX_cpd03561_e': 1000,
 'EX_cpd03696_e': 1000.0,
 'EX_cpd03724_e': 1000.0,
 'EX_cpd03725_e': 1000.0,
 'EX_cpd04097_e': 1000.0,
 'EX_cpd05158_e': 1000,
 'EX_cpd05264_e': 1000,
 'EX_cpd08305_e': 1000.0,
 'EX_cpd08306_e': 1000.0,
 'EX_cpd10515_e': 1000.0,
 'EX_cpd10516_e': 1000.0,
 'EX_cpd11576_e': 1000.0,
 'EX_cpd11594_e': 1000,
 'EX_cpd11597_e': 1000.0,
 'EX_cpd15584_e': 1000,
 'EX_cpd19001_e': 1000}

That's a lot of open exchange reactions! Let's make them a bit more realistic for an in vitro situation. We'll load a file specifying the base composition of the media in biolog single C/N growth conditions, and set the media conditions to reflect that. The base composition is missing a carbon source, so we'll enable uptake of glucose. In the medium dictionary, the numbers for each exchange reaction are uptake rates. If you inspect the actual exchange reactions, you will find that the equivalent to an uptake rate of 1000 units is a lower bound of -1000, because our exchange reactions are defined with the boundary metabolite as the reactant, e.g. cpd00182_e -->.


In [5]:
import pandas as pd
biolog_base = pd.read_csv("../medusa/test/data/biolog_base_composition.csv", sep=",")
biolog_base


Out[5]:
Name ID
0 H2O cpd00001_e
1 O2 cpd00007_e
2 Phosphate cpd00009_e
3 CO2 cpd00011_e
4 NH3 cpd00013_e
5 Mn2+ cpd00030_e
6 Zn2+ cpd00034_e
7 Sulfate cpd00048_e
8 Cu2+ cpd00058_e
9 Ca2+ cpd00063_e
10 H+ cpd00067_e
11 Cl- cpd00099_e
12 Co2+ cpd00149_e
13 K+ cpd00205_e
14 Mg cpd00254_e
15 Na+ cpd00971_e
16 Fe2+ cpd10515_e
17 fe3 cpd10516_e
18 Heme cpd00028_e
19 H2S2O3 cpd00268_e

In [6]:
# convert the biolog base to a dictionary, which we can use to set ensemble.base_model.medium directly.
biolog_base = {'EX_'+component:1000 for component in biolog_base['ID']}

# add glucose uptake to the new medium dictionary
biolog_base['EX_cpd00182_e'] = 10

# Set the medium on the base model
ensemble.base_model.medium = biolog_base
ensemble.base_model.medium


Out[6]:
{'EX_cpd00001_e': 1000,
 'EX_cpd00007_e': 1000,
 'EX_cpd00009_e': 1000,
 'EX_cpd00011_e': 1000,
 'EX_cpd00013_e': 1000,
 'EX_cpd00028_e': 1000,
 'EX_cpd00030_e': 1000,
 'EX_cpd00034_e': 1000,
 'EX_cpd00048_e': 1000,
 'EX_cpd00058_e': 1000,
 'EX_cpd00063_e': 1000,
 'EX_cpd00067_e': 1000,
 'EX_cpd00099_e': 1000,
 'EX_cpd00149_e': 1000,
 'EX_cpd00182_e': 10,
 'EX_cpd00205_e': 1000,
 'EX_cpd00254_e': 1000,
 'EX_cpd00268_e': 1000,
 'EX_cpd00971_e': 1000,
 'EX_cpd10515_e': 1000,
 'EX_cpd10516_e': 1000}

With the medium set, we can now simulate growth in these conditions:


In [7]:
from medusa.flux_analysis import flux_balance
fluxes = flux_balance.optimize_ensemble(ensemble,return_flux='bio1')

In [16]:
# get fluxes for the first 10 members
fluxes.head(10)


Out[16]:
bio1
Staphylococcus aureus_gapfilled_18 14.890551
Staphylococcus aureus_gapfilled_477 12.218825
Staphylococcus aureus_gapfilled_430 19.198765
Staphylococcus aureus_gapfilled_735 14.875922
Staphylococcus aureus_gapfilled_916 12.223456
Staphylococcus aureus_gapfilled_983 19.375070
Staphylococcus aureus_gapfilled_371 13.113148
Staphylococcus aureus_gapfilled_255 12.223456
Staphylococcus aureus_gapfilled_729 14.891239
Staphylococcus aureus_gapfilled_925 19.198765

In [10]:
import matplotlib.pylab as plt
fig, ax = plt.subplots()
plt.hist(fluxes['bio1'])
ax.set_ylabel('# ensemble members')
ax.set_xlabel('Flux through biomass reaction')
plt.show()


You may want to perform simulations with only a subset of ensemble members. There are two options for this; either identifying the desired members for simulation with the specific_models parameter, or passing a number of random members to perform simulations with the num_models parameter.


In [14]:
# perform FBA with a random set of 10 members:
subflux = flux_balance.optimize_ensemble(ensemble, num_models = 10, return_flux = "bio1")
subflux


Out[14]:
bio1
Staphylococcus aureus_gapfilled_300 18.441010
Staphylococcus aureus_gapfilled_181 14.875922
Staphylococcus aureus_gapfilled_667 17.618230
Staphylococcus aureus_gapfilled_668 14.875922
Staphylococcus aureus_gapfilled_639 14.186860
Staphylococcus aureus_gapfilled_636 14.186860
Staphylococcus aureus_gapfilled_738 14.643953
Staphylococcus aureus_gapfilled_68 12.223456
Staphylococcus aureus_gapfilled_87 14.875922
Staphylococcus aureus_gapfilled_580 12.223456

In [15]:
submembers = [member.id for member in ensemble.members[0:10]]
print(submembers)
subflux = flux_balance.optimize_ensemble(ensemble, specific_models = submembers, return_flux = "bio1")
subflux


['Staphylococcus aureus_gapfilled_892', 'Staphylococcus aureus_gapfilled_851', 'Staphylococcus aureus_gapfilled_501', 'Staphylococcus aureus_gapfilled_927', 'Staphylococcus aureus_gapfilled_875', 'Staphylococcus aureus_gapfilled_500', 'Staphylococcus aureus_gapfilled_751', 'Staphylococcus aureus_gapfilled_849', 'Staphylococcus aureus_gapfilled_372', 'Staphylococcus aureus_gapfilled_421']
Out[15]:
bio1
Staphylococcus aureus_gapfilled_372 12.223456
Staphylococcus aureus_gapfilled_421 13.113148
Staphylococcus aureus_gapfilled_500 19.198765
Staphylococcus aureus_gapfilled_501 12.223456
Staphylococcus aureus_gapfilled_751 14.209162
Staphylococcus aureus_gapfilled_849 12.224814
Staphylococcus aureus_gapfilled_851 19.375070
Staphylococcus aureus_gapfilled_875 17.872504
Staphylococcus aureus_gapfilled_892 19.375070
Staphylococcus aureus_gapfilled_927 19.198765

Flux Variability Analysis


In [ ]:

Gene and Reaction Deletions


In [ ]: