Analyze Steady-State Community COBRA Models

Author(s): Siu Hung Joshua Chan, Department of Chemical Engineering, The Pennsylvania State University

Reviewer(s): Almut Heinken, Luxembourg Centre for Systems Biomedicine, University of Luxembourg

INTRODUCTION

This tutorial demonstrates the use of SteadyCom to analyze a multi-organism COBRA model (e.g., for a microbial community) at a community steady-state [1]. Compared to the direct extension of flux balance analysis (FBA) which simply treats a community model as a multi-compartment model, SteadyCom explicitly introduces the biomass variables to describe the relationships between biomass, biomass production rate, growth rate and fluxes. SteadyCom also assumes the existence of a time-averaged population steady-state for a stable microbial community which in turn implies a time-averaged constant growth rate across all members. SteadyCom is equivalent to the reformulation of the earlier community flux balance analysis (cFBA) [2] with significant computational advantage. SteadyCom computes the maximum community growth rate by solving the follow optimization problem:
where is the stoichiometry of metabolite i in reaction j for organism k, , and are respectively the flux (in mmol/h), lower bound (in mmol/h/gdw) and upper bound (in mmol/h/gdw) for reaction j for organism k, is the community uptake bound for metabolite i, is the biomass (in gdw) of organism k, μ is the community growth rate, is the set of metabolites of organism k, is the set of community metabolites in the community exchange space, is the set of reactions for organism k, is the set of organisms in the community, and is the exchange reaction in organism k for extracellular metabolite i. See ref. [1] for the derivation and detailed explanation.
Throughout the tutorial, using a hypothetical model of four E. coli mutants auxotrophic for amino acids, we will demonstrate the three different functionalities of the module: (1) computing the maximum community growth rate using the function SteadyCom.m, (2) performing flux variability analysis under a given community growth rate using SteadyComFVA.m, and (3) analyzing the pairwise relationship between flux/biomass variables using a technique similar to Pareto-optimal analysis by calling the function SteadyComPOA.m

EQUIPMENT SETUP

If necessary, initialise the cobra toolbox and select a solver by running:
initCobraToolbox(false) % false, as we don't want to update
_____ _____ _____ _____ _____ | / ___| / _ \ | _ \ | _ \ / ___ \ | COnstraint-Based Reconstruction and Analysis | | | | | | | |_| | | |_| | | |___| | | The COBRA Toolbox - 2017 | | | | | | | _ { | _ / | ___ | | | |___ | |_| | | |_| | | | \ \ | | | | | Documentation: \_____| \_____/ |_____/ |_| \_\ |_| |_| | http://opencobra.github.io/cobratoolbox | > Checking if git is installed ... Done. > Checking if the repository is tracked using git ... Done. > Checking if curl is installed ... Done. > Checking if remote can be reached ... Done. > Initializing and updating submodules ... Done. > Adding all the files of The COBRA Toolbox ... Done. > Define CB map output... set to svg. > Retrieving models ... Done. > TranslateSBML is installed and working properly. > Configuring solver environment variables ... - [*---] ILOG_CPLEX_PATH: /Applications/IBM/ILOG/CPLEX_Studio1271/cplex/matlab/x86-64_osx - [*---] GUROBI_PATH: /Library/gurobi650/mac64/matlab - [----] TOMLAB_PATH : --> set this path manually after installing the solver ( see instructions ) - [----] MOSEK_PATH : --> set this path manually after installing the solver ( see instructions ) Done. > Checking available solvers and solver interfaces ... Done. > Setting default solvers ... Done. > Saving the MATLAB path ... Done. - The MATLAB path was saved in the default location. > Summary of available solvers and solver interfaces Support LP MILP QP MIQP NLP ---------------------------------------------------------------------- cplex_direct active 0 0 0 0 - dqqMinos active 1 - - - - glpk active 1 1 - - - gurobi active 1 1 1 1 - ibm_cplex active 1 1 1 - - matlab active 1 - - - 1 mosek active 0 0 0 - - pdco active 1 - 1 - - quadMinos active 1 - - - 1 tomlab_cplex active 0 0 0 0 - qpng passive - - 1 - - tomlab_snopt passive - - - - 0 gurobi_mex legacy 0 0 0 0 - lindo_old legacy 0 - - - - lindo_legacy legacy 0 - - - - lp_solve legacy 1 - - - - opti legacy 0 0 0 0 0 ---------------------------------------------------------------------- Total - 8 3 4 1 2 + Legend: - = not applicable, 0 = solver not compatible or not installed, 1 = solver installed. > You can solve LP problems using: 'dqqMinos' - 'glpk' - 'gurobi' - 'ibm_cplex' - 'matlab' - 'pdco' - 'quadMinos' - 'lp_solve' > You can solve MILP problems using: 'glpk' - 'gurobi' - 'ibm_cplex' > You can solve QP problems using: 'gurobi' - 'ibm_cplex' - 'pdco' - 'qpng' > You can solve MIQP problems using: 'gurobi' > You can solve NLP problems using: 'matlab' - 'quadMinos' > Checking for available updates ... --> You cannot update your fork using updateCobraToolbox(). [97ac46 @ master]. Please use the MATLAB.devTools (https://github.com/opencobra/MATLAB.devTools).
All SteadyCom functions involve only solving linear programming problems. Any solvers supported by the COBRA toolbox will work. But SteadyCom contains specialized codes for IBM ILOG Cplex which was tested to run significantly faster for SteadyComFVA and SteadyComPOA for larger problems through calling the Cplex object in Matlab directly.
Please note that parallelization requires a working installation of the Parallel Computing Toolbox.
changeCobraSolver('ibm_cplex', 'LP');
> IBM ILOG CPLEX interface added to MATLAB path.

PROCEDURE

Model Construction

Load the E. coli iAF1260 model in the COBRA toolbox.
global CBTDIR
iAF1260 = readCbModel([CBTDIR filesep 'test' filesep 'models' filesep 'iAF1260.mat']);
Polish the model a little bit:
% convert the compartment format from e.g., '_c' to '[c]'
iAF1260.mets = regexprep(iAF1260.mets, '_([^_]+)$', '\[$1\]');
% make all empty cells in cell arrays to be empty string
fieldToBeCellStr = {'metFormulas'; 'genes'; 'grRules'; 'metNames'; 'rxnNames'; 'subSystems'};
for j = 1:numel(fieldToBeCellStr)
iAF1260.(fieldToBeCellStr{j})(cellfun(@isempty, iAF1260.(fieldToBeCellStr{j}))) = {''};
end
Add a methionine export reaction to allow the export of methionine.
iAF1260 = addReaction(iAF1260,{'METt3pp',''},'met__L[c] + h[c] => met__L[p] + h[p]');
METt3pp h[c] + met__L[c] -> h[p] + met__L[p]
Reactions essential for amino acid autotrophy:
argH = {'ARGSL'}; % essential for arginine biosynthesis
lysA = {'DAPDC'}; % essential for lysine biosynthesis
metA = {'HSST'}; % essential for methionine biosynthesis
ilvE = {'PPNDH'}; % essential for phenylalanine biosynthesis
Reactions essential for exporting amino acids:
argO = {'ARGt3pp'}; % Evidence for an arginine exporter encoded by yggA (argO) that is regulated by the LysR-type transcriptional regulator ArgP in Escherichia coli.
lysO = {'LYSt3pp'}; % Distinct paths for basic amino acid export in Escherichia coli: YbjE (LysO) mediates export of L-lysine
yjeH = {'METt3pp'}; % YjeH is a novel L-methionine and branched chain amino acids exporter in Escherichia coli
yddG = {'PHEt2rpp'}; % YddG from Escherichia coli promotes export of aromatic amino acids.
Now make four copies of the model with auxotrophy for different amino acids and inability to export amino acids:
% auxotrophic for Lys and Met, not exporting Phe
Ec1 = iAF1260;
Ec1 = changeRxnBounds(Ec1, [lysA; metA; yddG], 0, 'b');
% auxotrophic for Arg and Phe, not exporting Met
Ec2 = iAF1260;
Ec2 = changeRxnBounds(Ec2, [argH; yjeH; ilvE], 0, 'b');
% Auxotrophic for Arg and Phe, not exporting Lys
Ec3 = iAF1260;
Ec3 = changeRxnBounds(Ec3, [argH; lysO; ilvE], 0, 'b');
% Auxotrophic for Lys and Met, not exporting Arg
Ec4 = iAF1260;
Ec4 = changeRxnBounds(Ec4, [argO; lysA; metA], 0, 'b');
Now none of the four organisms can grow alone and they must cross feed each other to survive. See Figure 1 in ref. [1] for the visualization of the community.
Get the extracellular metabolites, the corresponding exchange reactions and the uptake rates for the E. coli model, which are used later to constrain the community model:
% extracellular metabolites (met[e])
metEx = strcmp(getCompartment(iAF1260.mets),'e');
% the corresponding exchange reactions
rxnExAll = find(sum(iAF1260.S ~= 0, 1) == 1);
[rxnEx, ~] = find(iAF1260.S(metEx, rxnExAll)'); % need to be in the same order as metEx
rxnEx = rxnExAll(rxnEx);
% exchange rate
lbEx = iAF1260.lb(rxnEx);
Create a community model with the four E. coli tagged as 'Ec1', 'Ec2', 'Ec3', 'Ec4' respectively by calling createMultipleSpeciesModel.
nameTagsModel = {'Ec1'; 'Ec2'; 'Ec3'; 'Ec4'};
EcCom = createMultipleSpeciesModel({Ec1; Ec2; Ec3; Ec4}, nameTagsModel);
EcCom.csense = char('E' * ones(1,numel(EcCom.mets))); % correct the csense
clear Ec1 Ec2 Ec3 Ec4
The model EcCom contains a community compartment denoted by [u] to allow exchange between organisms. Each organism-specific reaction/metabolite is prepended with the corresponding tag.
Retreive the names and ids for organism/community exchange reactions/metabolites which are necessary for computation:
[EcCom.infoCom, EcCom.indCom] = getMultiSpeciesModelId(EcCom, nameTagsModel);
disp(EcCom.infoCom);
EcCom.infoCom contains reaction/metabolite names (from EcCom.rxns/EcCom.mets) for the community exchange reactions (*.EXcom), organism-community exchange reactions (*.EXsp), community metabolites (*.Mcom), organism-specific extracellular metabolite (*.Msp). If a host model is specified, there will also be non-empty *.EXhost and *.Mhost for the host-specific exchange reactions and metabolites. The fields *.rxnSps/*.metSps give information on which organism a reaction/metabolite belongs to.
indCom has the same structure as infoCom but contains the indices rather than names. infoCom and indCom are attached as fields of the model EcCom because SteadyCom requires this information from the input model for computation. Incorporate also the names and indices for the biomass reactions which are necessary for computing growth:
rxnBiomass = strcat(nameTagsModel, 'BIOMASS_Ec_iAF1260_core_59p81M'); % biomass reaction names
rxnBiomassId = findRxnIDs(EcCom, rxnBiomass); % ids
EcCom.infoCom.spBm = rxnBiomass; % .spBm for organism biomass reactions
EcCom.indCom.spBm = rxnBiomassId;

Finding Maximum Growth Rate Using SteadyCom

Set community and organism-specific uptake rates to be the same as in the orginal iAF1260 model:
[yn, id] = ismember(strrep(iAF1260.mets(metEx), '[e]', '[u]'), EcCom.infoCom.Mcom); % map the metabolite name
assert(all(yn)); % must be a 1-to-1 mapping
EcCom.lb(EcCom.indCom.EXcom(:,1)) = lbEx(id); % assign community uptake bounds
EcCom.ub(EcCom.indCom.EXcom(:,1)) = 1e5;
EcCom.lb(EcCom.indCom.EXsp) = repmat(lbEx(id), 1, 4); % assign organism-specific uptake bounds
Set maximum allowed organism-specific uptake rates for the cross-feeding amino acids:
% only allow to take up the amino acids that one is auxotrophic for
exRate = 1; % maximum uptake rate for cross feeding AAs
% Ec1
EcCom = changeRxnBounds(EcCom, {'Ec1IEX_arg__L[u]tr'; 'Ec1IEX_phe__L[u]tr'}, 0, 'l');
EcCom = changeRxnBounds(EcCom, {'Ec1IEX_met__L[u]tr'; 'Ec1IEX_lys__L[u]tr'}, -exRate, 'l');
% Ec2
EcCom = changeRxnBounds(EcCom, {'Ec2IEX_arg__L[u]tr'; 'Ec2IEX_phe__L[u]tr'}, -exRate, 'l');
EcCom = changeRxnBounds(EcCom, {'Ec2IEX_met__L[u]tr'; 'Ec2IEX_lys__L[u]tr'}, 0, 'l');
% Ec3
EcCom = changeRxnBounds(EcCom, {'Ec3IEX_arg__L[u]tr'; 'Ec3IEX_phe__L[u]tr'}, -exRate, 'l');
EcCom = changeRxnBounds(EcCom, {'Ec3IEX_met__L[u]tr'; 'Ec3IEX_lys__L[u]tr'}, 0, 'l');
% Ec4
EcCom = changeRxnBounds(EcCom, {'Ec4IEX_arg__L[u]tr'; 'Ec4IEX_phe__L[u]tr'}, 0, 'l');
EcCom = changeRxnBounds(EcCom, {'Ec4IEX_met__L[u]tr'; 'Ec4IEX_lys__L[u]tr'}, -exRate, 'l');
% allow production of anything for each member
EcCom.ub(EcCom.indCom.EXsp(:)) = 1000;
Before the calculation, print the community uptake bounds for checking using printUptakeBoundCom:
printUptakeBoundCom(EcCom, 1);
Mets Comm. Ec1 Ec2 Ec3 Ec4 ( 53) arg__L 0 0 -1 -1 0 ( 60) ca2 1e+06 -1e+06 -1e+06 -1e+06 -1e+06 ( 62) cbl1 0.01 -0.01 -0.01 -0.01 -0.01 ( 67) cl 1e+06 -1e+06 -1e+06 -1e+06 -1e+06 ( 69) co2 1e+06 -1e+06 -1e+06 -1e+06 -1e+06 ( 70) cobalt2 1e+06 -1e+06 -1e+06 -1e+06 -1e+06 ( 76) cu2 1e+06 -1e+06 -1e+06 -1e+06 -1e+06 (108) fe2 1e+06 -1e+06 -1e+06 -1e+06 -1e+06 (109) fe3 1e+06 -1e+06 -1e+06 -1e+06 -1e+06 (144) glc__D 8 -8 -8 -8 -8 (167) h2o 1e+06 -1e+06 -1e+06 -1e+06 -1e+06 (169) h 1e+06 -1e+06 -1e+06 -1e+06 -1e+06 (186) k 1e+06 -1e+06 -1e+06 -1e+06 -1e+06 (194) lys__L 0 -1 0 0 -1 (208) met__L 0 -1 0 0 -1 (211) mg2 1e+06 -1e+06 -1e+06 -1e+06 -1e+06 (214) mn2 1e+06 -1e+06 -1e+06 -1e+06 -1e+06 (216) mobd 1e+06 -1e+06 -1e+06 -1e+06 -1e+06 (219) na1 1e+06 -1e+06 -1e+06 -1e+06 -1e+06 (221) nh4 1e+06 -1e+06 -1e+06 -1e+06 -1e+06 (228) o2 18.5 -18.5 -18.5 -18.5 -18.5 (237) phe__L 0 0 -1 -1 0 (239) pi 1e+06 -1e+06 -1e+06 -1e+06 -1e+06 (260) so4 1e+06 -1e+06 -1e+06 -1e+06 -1e+06 (280) tungs 1e+06 -1e+06 -1e+06 -1e+06 -1e+06 (299) zn2 1e+06 -1e+06 -1e+06 -1e+06 -1e+06
Values under 'Comm.' are the community uptake bounds (+ve for uptake) and values under 'Ec1' are the Ec1-specific uptake bounds (-ve for uptake).
Create an option structure for calling SteadyCom and call the function. There are a range of options available, including setting algorithmic parameters, fixing growth rates for members, adding additional linear constraints in a general format, e.g., for molecular crowding effect. See help SteadyCom for more options.
options = struct();
options.GRguess = 0.5; % initial guess for max. growth rate
options.GRtol = 1e-6; % tolerance for final growth rate
options.algorithm = 1; % use the default algorithm (simple guessing for bounds, followed by matlab fzero)
[sol, result] = SteadyCom(EcCom, options);
Find maximum community growth rate.. Model feasible at maintenance. Time elapsed: 1 / 1 sec Iter LB To test UB Time elapsed (iteration/total) 1 0.000000 0.500000 Inf 0 / 1 sec 2 0.500000 0.721279 Inf 4 / 5 sec 3 0.721279 0.735372 Inf 0 / 5 sec 4 0.735372 0.742726 Inf 0 / 5 sec Func-count x f(x) Procedure 2 0.735372 -0.000807615 initial 3 0.735378 -0.00079987 interpolation 4 0.73599 -1.26127e-06 interpolation 5 0.73599 -1.26127e-06 interpolation Zero found in the interval [0.735372, 0.742726] Maximum community growth rate: 0.735990 (abs. error < 1e-06). Time elapsed: 21 sec
The algorithm is an iterative procedure to find the maximum biomass at a given growth rate and to determine the maximum growth rate that is feasible for the required total biomass (default 1 gdw). Here the algorithm used is the simple guessing for find upper and lower bounds (Iter 1 to 4 in the output) followed by Matlab fzero (starting from the line 'Func-count') to locate the root. The maximum growth rate calculated is 0.73599 /h, stored in result.GRmax.
The biomass for each organism (in gdw) is given by result.BM:
for jSp = 1:4
fprintf('X_%s: %.6f\n', EcCom.infoCom.spAbbr{jSp}, result.BM(jSp));
end
X_Ec1: 0.253294
X_Ec2: 0.324611
X_Ec3: 0.185004
X_Ec4: 0.237093
disp(result);
GRmax: 0.7360 vBM: [4×1 double] BM: [4×1 double] Ut: [299×1 double] Ex: [299×1 double] flux: [9831×1 double] iter0: [0 11.4198 0 9.9476e-14] iter: [4×6 double] stat: 'optimal'
result.vBM contains the biomass production rates (in gdw / h), equal to result.BM * result.GRmax. Since the total community biomass is defaulted to be 1 gdw, the biomass for each organism coincides with its relative abundance. Note that the community uptake bounds in this sense are normalized per gdw of the community biomass. So the lower bound for the exchange reaction EX_glc__D[u] being 8 can be interpreted as the maximum amount of glucose available to the community being at a rate of 8 mmol per hour for 1 gdw of community biomass. Similarly, all fluxes in result.flux () has the unit mmol / h / [gdw of comm. biomass]. It differs from the specific rate (traditionally denoted by ) of an organism in the usual sense (in the unit of mmol / h / [gdw of organism biomass]) by where is the biomass of the organism. result.Ut and result.Ex are the community uptake and export rates respectively, corresponding to the exchange reactions in EcCom.infoCom.EXcom.
result.iter0 is the info for solving the model at zero growth rate and result.iter records the info during iteration of the algorithm:
 
iter = [0, result.iter0, NaN; result.iter];
for j = 0 : size(iter, 1)
if j == 0
fprintf('#iter\tgrowth rate (mu)\tmax. biomass (sum(X))\tmu * sum(X)\tmax. infeasibility\tguess method\n');
else
fprintf('%5d\t%16.6f\t%21.6f\t%11.6f\t%18.6e\t%d\n', iter(j,:))
end
end
#iter growth rate (mu) max. biomass (sum(X)) mu * sum(X) max. infeasibility guess method
0 0.000000 11.419845 0.000000 9.947598e-14 NaN
1 0.500000 1.442559 0.721279 3.493989e-10 0
2 0.721279 1.019539 0.735372 3.668634e-10 0
3 0.735372 1.000808 0.735966 1.706138e-10 0
4 0.742726 0.000000 0.000000 0.000000e+00 2
mu * sum(X) in the forth column is equal to the biomass production rate.
The fifth column contains the maximum infeasibility of the solutions in each iteration.
Guess method in the last column represents the method used for guessing the growth rate solved in the current iteration:
0: the default simple guess by (K is the total number of organisms)
1: bisection method
2: bisection or at least 1% away from the bounds if the simple guess is too close to the bounds (<1%)
3. 1% away from the current growth rate if the simple guess is too close to the current growth rate
From the table, we can see that at the growth rate 0.742726 (iter 4), the max. biomass is 0, while at growth rate 0.735372, max. biomass = 1.0008 > 1. Therefore we have both an lower and upper bound for the max. growth rate. Then fzero is initiated to solve for the max. growth rate that gives max. biomass >= 1.
Two other algorithms for the iterative procedure are also implemented: simple guessing only and the bisection method. Compare their results with simple guessing + matlab fzero run above:
options.algorithm = 2; % use the simple guessing algorithm
[sol2, result2] = SteadyCom(EcCom, options);
Find maximum community growth rate.. Model feasible at maintenance. Time elapsed: 1 / 1 sec Iter LB To test UB Time elapsed (iteration/total) 1 0.000000 0.500000 Inf 0 / 1 sec 2 0.500000 0.721279 Inf 4 / 5 sec 3 0.721279 0.735372 Inf 0 / 5 sec 4 0.735372 0.742726 Inf 0 / 5 sec 5 0.735372 0.739049 0.742726 0 / 5 sec 6 0.735372 0.737211 0.739049 0 / 5 sec 7 0.735372 0.736291 0.737211 0 / 5 sec 8 0.735372 0.735832 0.736291 0 / 6 sec 9 0.735832 0.736062 0.736291 1 / 7 sec 10 0.735832 0.735947 0.736062 0 / 7 sec 11 0.735947 0.736004 0.736062 1 / 8 sec 12 0.735947 0.735975 0.736004 0 / 8 sec 13 0.735975 0.735990 0.736004 2 / 10 sec 14 0.735990 0.735997 0.736004 0 / 10 sec 15 0.735990 0.735993 0.735997 0 / 10 sec 16 0.735990 0.735991 0.735993 0 / 11 sec 17 0.735990 0.735991 0.735991 0 / 11 sec Maximum community growth rate: 0.735991 (abs. error < 1e-06). Time elapsed: 14 sec
options.algorithm = 3; % use the bisection algorithm
[sol3, result3] = SteadyCom(EcCom, options);
Find maximum community growth rate.. Model feasible at maintenance. Time elapsed: 0 / 0 sec Iter LB To test UB Time elapsed (iteration/total) 1 0.000000 0.500000 Inf 0 / 0 sec 2 0.500000 1.000000 Inf 3 / 4 sec 3 0.500000 0.750000 1.000000 0 / 4 sec 4 0.500000 0.625000 0.750000 4 / 8 sec 5 0.625000 0.687500 0.750000 5 / 13 sec 6 0.687500 0.718750 0.750000 0 / 13 sec 7 0.718750 0.734375 0.750000 0 / 13 sec 8 0.734375 0.742188 0.750000 0 / 13 sec 9 0.734375 0.738281 0.742188 0 / 13 sec 10 0.734375 0.736328 0.738281 0 / 13 sec 11 0.734375 0.735352 0.736328 0 / 14 sec 12 0.735352 0.735840 0.736328 0 / 14 sec 13 0.735840 0.736084 0.736328 0 / 14 sec 14 0.735840 0.735962 0.736084 0 / 15 sec 15 0.735962 0.736023 0.736084 1 / 16 sec 16 0.735962 0.735992 0.736023 0 / 16 sec 17 0.735962 0.735977 0.735992 0 / 17 sec 18 0.735977 0.735985 0.735992 2 / 18 sec 19 0.735985 0.735989 0.735992 0 / 19 sec 20 0.735989 0.735991 0.735992 0 / 19 sec 21 0.735991 0.735991 0.735992 0 / 19 sec Maximum community growth rate: 0.735991 (abs. error < 1e-06). Time elapsed: 26 sec
The time used for each algorithm in the tested machine is:
(1) simple guess for bounds followed by Matlab fzero: 18 sec
(2) simple guess alone: 35 sec
(3) bisection: 70 sec
Algorithm (1) appears to be the fastest in most case although the simple guess algorithm can sometimes also outperform it. The most conservative bisection method can already guarantee convergence within around 20 iterations, i.e., solving ~20 LPs for an optimality gap (options.GRtol) of 1e-6.

Analyzing Flux Variability Using SteadyComFVA

Now we want to analyze the variability of the organism abundance at various growth rates. Choose more options and call SteadyComFVA:
% percentage of maximum total biomass of the community required. 100 for sum(biomass) = 1 (1 is the default total biomass)
options.optBMpercent = 100;
n = size(EcCom.S, 2); % number of reactions in the model
% options.rxnNameList is the list of reactions subject to FVA. Can be reaction names or indices.
% Use n + j for the biomass variable of the j-th organism. Alternatively, use {'X_j'}
% for biomass variable of the j-th organism or {'X_Ec1'} for Ec1 (the abbreviation in EcCom.infoCom.spAbbr)
options.rxnNameList = {'X_Ec1'; 'X_Ec2'; 'X_Ec3'; 'X_Ec4'};
options.optGRpercent = [89:0.2:99, 99.1:0.1:100]; % perform FVA at various percentages of the maximum growth rate, 89, 89.1, 89.2, ..., 100
[fvaComMin,fvaComMax] = SteadyComFVA(EcCom, options);
Find maximum community growth rate.. Model feasible at maintenance. Time elapsed: 1 / 1 sec Iter LB To test UB Time elapsed (iteration/total) 1 0.000000 0.500000 Inf 0 / 1 sec 2 0.500000 1.000000 Inf 4 / 5 sec 3 0.500000 0.750000 1.000000 0 / 5 sec 4 0.500000 0.625000 0.750000 5 / 11 sec 5 0.625000 0.687500 0.750000 7 / 17 sec 6 0.687500 0.718750 0.750000 0 / 17 sec 7 0.718750 0.734375 0.750000 0 / 17 sec 8 0.734375 0.742188 0.750000 0 / 18 sec 9 0.734375 0.738281 0.742188 0 / 18 sec 10 0.734375 0.736328 0.738281 0 / 18 sec 11 0.734375 0.735352 0.736328 0 / 18 sec 12 0.735352 0.735840 0.736328 0 / 19 sec 13 0.735840 0.736084 0.736328 0 / 19 sec 14 0.735840 0.735962 0.736084 0 / 19 sec 15 0.735962 0.736023 0.736084 2 / 21 sec 16 0.735962 0.735992 0.736023 0 / 21 sec 17 0.735962 0.735977 0.735992 0 / 22 sec 18 0.735977 0.735985 0.735992 2 / 24 sec 19 0.735985 0.735989 0.735992 0 / 24 sec 20 0.735989 0.735991 0.735992 0 / 24 sec 21 0.735991 0.735991 0.735992 0 / 24 sec Maximum community growth rate: 0.735991 (abs. error < 1e-06). Time elapsed: 33 sec FVA for 4 sets of fluxes/biomass at growth rate 0.655032 : No % Name Min Max 1 25 X_Ec1 0.044053 0.787578 2 50 X_Ec2 0.038253 0.720492 3 75 X_Ec3 0.021200 0.696956 4 100 X_Ec4 0.029222 0.697238 BMmax adjustment: 1 BMmax adjustment: 2 BMmax adjustment: 3 BMmax adjustment: 4 BMmax adjustment: 5 BMmax adjustment: 6 BMmax adjustment: 7 BMmax adjustment: 8 BMmax adjustment: 9 BMmax adjustment: 10
Warning: Model not feasible.
FVA for 4 sets of fluxes/biomass at growth rate 0.657976 : No % Name Min Max 1 25 X_Ec1 0.046186 0.783368 2 50 X_Ec2 0.039919 0.713899 3 75 X_Ec3 0.022092 0.689206 4 100 X_Ec4 0.030498 0.689833 FVA for 4 sets of fluxes/biomass at growth rate 0.659448 : No % Name Min Max 1 25 X_Ec1 0.047304 0.781210 2 50 X_Ec2 0.040788 0.710505 3 75 X_Ec3 0.022556 0.685205 4 100 X_Ec4 0.031163 0.686016 FVA for 4 sets of fluxes/biomass at growth rate 0.660919 : No % Name Min Max 1 25 X_Ec1 0.048458 0.779016 2 50 X_Ec2 0.041682 0.707043 3 75 X_Ec3 0.023033 0.681117 4 100 X_Ec4 0.031848 0.682120 FVA for 4 sets of fluxes/biomass at growth rate 0.662391 : No % Name Min Max 1 25 X_Ec1 0.049649 0.776783 2 50 X_Ec2 0.042603 0.703511 3 75 X_Ec3 0.023523 0.676937 4 100 X_Ec4 0.032553 0.678142 BMmax adjustment: 1 FVA for 4 sets of fluxes/biomass at growth rate 0.663863 : No % Name Min Max 1 25 X_Ec1 0.050880 0.774509 2 50 X_Ec2 0.043552 0.699897 3 75 X_Ec3 0.024028 0.672653 4 100 X_Ec4 0.033283 0.674078 FVA for 4 sets of fluxes/biomass at growth rate 0.665335 : No % Name Min Max 1 25 X_Ec1 0.052152 0.772192 2 50 X_Ec2 0.044530 0.696203 3 75 X_Ec3 0.024547 0.668265 4 100 X_Ec4 0.034036 0.669928 FVA for 4 sets of fluxes/biomass at growth rate 0.666807 : No % Name Min Max 1 25 X_Ec1 0.053466 0.769834 2 50 X_Ec2 0.045538 0.692431 3 75 X_Ec3 0.025082 0.663776 4 100 X_Ec4 0.034812 0.665686 FVA for 4 sets of fluxes/biomass at growth rate 0.668279 : No % Name Min Max 1 25 X_Ec1 0.054825 NaN 2 50 X_Ec2 0.046576 NaN 3 75 X_Ec3 NaN 0.659181 4 100 X_Ec4 0.035612 0.661351 FVA for 4 sets of fluxes/biomass at growth rate 0.669751 : No % Name Min Max 1 25 X_Ec1 0.056231 0.764988 2 50 X_Ec2 0.047646 0.684644 3 75 X_Ec3 0.026197 NaN 4 100 X_Ec4 0.036437 0.656920 BMmax adjustment: 1 BMmax adjustment: 2 BMmax adjustment: 3 BMmax adjustment: 4 BMmax adjustment: 5 BMmax adjustment: 6 FVA for 4 sets of fluxes/biomass at growth rate 0.671223 : No % Name Min Max 1 25 X_Ec1 0.057686 NaN 2 50 X_Ec2 0.048750 0.680624 3 75 X_Ec3 0.026779 NaN 4 100 X_Ec4 0.037288 0.652387 FVA for 4 sets of fluxes/biomass at growth rate 0.672695 : No % Name Min Max 1 25 X_Ec1 0.059191 0.759959 2 50 X_Ec2 0.049888 0.676516 3 75 X_Ec3 0.027379 NaN 4 100 X_Ec4 0.038166 0.647752 FVA for 4 sets of fluxes/biomass at growth rate 0.674167 : No % Name Min Max 1 25 X_Ec1 0.060750 NaN 2 50 X_Ec2 0.051063 0.672316 3 75 X_Ec3 0.027996 NaN 4 100 X_Ec4 0.039073 0.643008 FVA for 4 sets of fluxes/biomass at growth rate 0.675639 : No % Name Min Max 1 25 X_Ec1 0.062365 NaN 2 50 X_Ec2 0.052275 0.668022 3 75 X_Ec3 0.028632 0.634496 4 100 X_Ec4 0.040009 NaN FVA for 4 sets of fluxes/biomass at growth rate 0.677111 : No % Name Min Max 1 25 X_Ec1 0.064038 0.752047 2 50 X_Ec2 0.053526 0.663629 3 75 X_Ec3 0.029287 NaN 4 100 X_Ec4 0.040976 0.633183 FVA for 4 sets of fluxes/biomass at growth rate 0.678583 : No % Name Min Max 1 25 X_Ec1 0.065772 0.749305 2 50 X_Ec2 0.054818 0.659135 3 75 X_Ec3 0.029963 0.623739 4 100 X_Ec4 0.041975 0.628092 FVA for 4 sets of fluxes/biomass at growth rate 0.680055 : No % Name Min Max 1 25 X_Ec1 0.067571 0.746507 2 50 X_Ec2 0.056153 0.654536 3 75 X_Ec3 0.030659 0.618150 4 100 X_Ec4 0.043007 0.622877 BMmax adjustment: 1 BMmax adjustment: 2 BMmax adjustment: 3 FVA for 4 sets of fluxes/biomass at growth rate 0.681527 : No % Name Min Max 1 25 X_Ec1 0.069437 NaN 2 50 X_Ec2 0.057533 0.649827 3 75 X_Ec3 0.031377 0.612415 4 100 X_Ec4 0.044075 0.617533 FVA for 4 sets of fluxes/biomass at growth rate 0.682999 : No % Name Min Max 1 25 X_Ec1 0.071373 NaN 2 50 X_Ec2 0.058959 0.645006 3 75 X_Ec3 0.032118 0.606527 4 100 X_Ec4 0.045179 0.612055 FVA for 4 sets of fluxes/biomass at growth rate 0.684471 : No % Name Min Max 1 25 X_Ec1 0.073384 NaN 2 50 X_Ec2 0.060434 0.640067 3 75 X_Ec3 0.032882 0.600479 4 100 X_Ec4 0.046322 0.606437 FVA for 4 sets of fluxes/biomass at growth rate 0.685943 : No % Name Min Max 1 25 X_Ec1 0.075473 0.734721 2 50 X_Ec2 0.061960 0.635005 3 75 X_Ec3 0.033672 0.594264 4 100 X_Ec4 0.047505 0.600674 FVA for 4 sets of fluxes/biomass at growth rate 0.687415 : No % Name Min Max 1 25 X_Ec1 0.077644 0.731615 2 50 X_Ec2 0.063539 0.629817 3 75 X_Ec3 0.034486 0.587876 4 100 X_Ec4 0.048731 0.594760 FVA for 4 sets of fluxes/biomass at growth rate 0.688887 : No % Name Min Max 1 25 X_Ec1 0.079901 0.728440 2 50 X_Ec2 0.065174 0.624497 3 75 X_Ec3 0.035328 0.581308 4 100 X_Ec4 0.050000 0.588689 FVA for 4 sets of fluxes/biomass at growth rate 0.690359 : No % Name Min Max 1 25 X_Ec1 0.082249 0.725194 2 50 X_Ec2 0.066868 0.619039 3 75 X_Ec3 0.036197 0.574550 4 100 X_Ec4 0.051316 0.582454 FVA for 4 sets of fluxes/biomass at growth rate 0.691831 : No % Name Min Max 1 25 X_Ec1 0.084698 0.721873 2 50 X_Ec2 0.068624 0.613425 3 75 X_Ec3 0.037096 0.567595 4 100 X_Ec4 0.052681 0.576024 BMmax adjustment: 1 BMmax adjustment: 2 BMmax adjustment: 3 BMmax adjustment: 4 BMmax adjustment: 5 BMmax adjustment: 6 BMmax adjustment: 7 BMmax adjustment: 8 BMmax adjustment: 9 BMmax adjustment: 10
Warning: Model not feasible.
BMmax adjustment: 1 BMmax adjustment: 2 BMmax adjustment: 3 BMmax adjustment: 4 BMmax adjustment: 5 BMmax adjustment: 6 BMmax adjustment: 7 BMmax adjustment: 8 BMmax adjustment: 9 BMmax adjustment: 10
Warning: Model not feasible.
BMmax adjustment: 1 BMmax adjustment: 2 BMmax adjustment: 3 BMmax adjustment: 4 BMmax adjustment: 5 BMmax adjustment: 6 BMmax adjustment: 7 BMmax adjustment: 8 BMmax adjustment: 9 FVA for 4 sets of fluxes/biomass at growth rate 0.696247 : No % Name Min Max 1 25 X_Ec1 0.092676 0.711435 2 50 X_Ec2 0.074290 0.595651 3 75 X_Ec3 0.039980 0.545450 4 100 X_Ec4 0.057093 0.555620 FVA for 4 sets of fluxes/biomass at growth rate 0.697719 : No % Name Min Max 1 25 X_Ec1 0.095566 0.707786 2 50 X_Ec2 0.076323 0.589407 3 75 X_Ec3 0.041009 0.537609 4 100 X_Ec4 0.058679 0.548420 FVA for 4 sets of fluxes/biomass at growth rate 0.699191 : No % Name Min Max 1 25 X_Ec1 0.098582 NaN 2 50 X_Ec2 0.078435 0.583010 3 75 X_Ec3 0.042075 0.529518 4 100 X_Ec4 0.060328 0.541006 FVA for 4 sets of fluxes/biomass at growth rate 0.700663 : No % Name Min Max 1 25 X_Ec1 0.101732 0.700210 2 50 X_Ec2 0.080630 0.576441 3 75 X_Ec3 0.043179 0.521166 4 100 X_Ec4 0.062043 0.533368 FVA for 4 sets of fluxes/biomass at growth rate 0.702135 : No % Name Min Max 1 25 X_Ec1 0.105023 0.696276 2 50 X_Ec2 0.082912 0.569710 3 75 X_Ec3 0.044323 0.512540 4 100 X_Ec4 0.063828 0.525494 BMmax adjustment: 1 BMmax adjustment: 2 BMmax adjustment: 3 BMmax adjustment: 4 BMmax adjustment: 5 BMmax adjustment: 6 BMmax adjustment: 7 BMmax adjustment: 8 BMmax adjustment: 9 BMmax adjustment: 10
Warning: Model not feasible.
BMmax adjustment: 1 BMmax adjustment: 2 FVA for 4 sets of fluxes/biomass at growth rate 0.705079 : No % Name Min Max 1 25 X_Ec1 0.112067 NaN 2 50 X_Ec2 0.087757 0.555814 3 75 X_Ec3 0.046739 0.494406 4 100 X_Ec4 0.067624 0.508993 FVA for 4 sets of fluxes/biomass at growth rate 0.706551 : No % Name Min Max 1 25 X_Ec1 0.115837 0.683829 2 50 X_Ec2 0.090331 0.548563 3 75 X_Ec3 0.048016 0.484867 4 100 X_Ec4 0.069643 0.500341 FVA for 4 sets of fluxes/biomass at growth rate 0.708023 : No % Name Min Max 1 25 X_Ec1 0.119788 0.679449 2 50 X_Ec2 0.093013 0.541098 3 75 X_Ec3 0.049341 0.474990 4 100 X_Ec4 0.071750 0.491402 FVA for 4 sets of fluxes/biomass at growth rate 0.709495 : No % Name Min Max 1 25 X_Ec1 0.123931 0.674943 2 50 X_Ec2 0.095810 0.533410 3 75 X_Ec3 0.050717 0.464757 4 100 X_Ec4 0.073950 0.482162 FVA for 4 sets of fluxes/biomass at growth rate 0.710967 : No % Name Min Max 1 25 X_Ec1 0.128278 0.670305 2 50 X_Ec2 0.098728 0.525487 3 75 X_Ec3 0.052147 0.454147 4 100 X_Ec4 0.076248 0.472603 FVA for 4 sets of fluxes/biomass at growth rate 0.712439 : No % Name Min Max 1 25 X_Ec1 0.132843 0.665529 2 50 X_Ec2 0.101775 0.517320 3 75 X_Ec3 0.053634 0.443138 4 100 X_Ec4 0.078651 0.462710 BMmax adjustment: 1 BMmax adjustment: 2 BMmax adjustment: 3 BMmax adjustment: 4 BMmax adjustment: 5 BMmax adjustment: 6 FVA for 4 sets of fluxes/biomass at growth rate 0.713911 : No % Name Min Max 1 25 X_Ec1 0.137641 0.660607 2 50 X_Ec2 0.104958 0.508895 3 75 X_Ec3 0.055181 0.431707 4 100 X_Ec4 0.081165 0.452462 FVA for 4 sets of fluxes/biomass at growth rate 0.715383 : No % Name Min Max 1 25 X_Ec1 0.142688 0.655531 2 50 X_Ec2 0.108287 0.500202 3 75 X_Ec3 0.056790 0.419828 4 100 X_Ec4 0.083798 0.441839 FVA for 4 sets of fluxes/biomass at growth rate 0.716855 : No % Name Min Max 1 25 X_Ec1 0.148002 0.650292 2 50 X_Ec2 0.111770 0.491225 3 75 X_Ec3 0.058466 0.407473 4 100 X_Ec4 0.086557 0.430821 FVA for 4 sets of fluxes/biomass at growth rate 0.718327 : No % Name Min Max 1 25 X_Ec1 0.153601 0.644881 2 50 X_Ec2 0.115417 0.481952 3 75 X_Ec3 0.060212 0.394612 4 100 X_Ec4 0.089450 0.419382 BMmax adjustment: 1 FVA for 4 sets of fluxes/biomass at growth rate 0.719799 : No % Name Min Max 1 25 X_Ec1 0.159507 0.639287 2 50 X_Ec2 0.119240 0.472366 3 75 X_Ec3 0.062032 0.381212 4 100 X_Ec4 0.092488 0.407496 FVA for 4 sets of fluxes/biomass at growth rate 0.721271 : No % Name Min Max 1 25 X_Ec1 0.165742 0.633501 2 50 X_Ec2 0.123249 0.462452 3 75 X_Ec3 0.063931 0.367237 4 100 X_Ec4 0.095680 0.395137 FVA for 4 sets of fluxes/biomass at growth rate 0.722743 : No % Name Min Max 1 25 X_Ec1 0.172333 0.627510 2 50 X_Ec2 0.127458 0.452192 3 75 X_Ec3 0.065912 0.352649 4 100 X_Ec4 0.099037 0.382274 BMmax adjustment: 1 BMmax adjustment: 2 FVA for 4 sets of fluxes/biomass at growth rate 0.724215 : No % Name Min Max 1 25 X_Ec1 0.179305 0.621301 2 50 X_Ec2 0.131880 0.441568 3 75 X_Ec3 0.067982 0.337405 4 100 X_Ec4 0.102572 0.368873 FVA for 4 sets of fluxes/biomass at growth rate 0.725687 : No % Name Min Max 1 25 X_Ec1 0.186691 0.614859 2 50 X_Ec2 0.136531 0.430558 3 75 X_Ec3 0.070145 0.321457 4 100 X_Ec4 0.106297 0.354898 FVA for 4 sets of fluxes/biomass at growth rate 0.727159 : No % Name Min Max 1 25 X_Ec1 0.194523 NaN 2 50 X_Ec2 0.141428 0.419142 3 75 X_Ec3 0.072407 0.304754 4 100 X_Ec4 0.110228 0.340309 FVA for 4 sets of fluxes/biomass at growth rate 0.728631 : No % Name Min Max 1 25 X_Ec1 0.202839 0.601215 2 50 X_Ec2 0.146588 0.407296 3 75 X_Ec3 0.074774 0.287239 4 100 X_Ec4 0.114380 0.325063 FVA for 4 sets of fluxes/biomass at growth rate 0.729367 : No % Name Min Max 1 25 X_Ec1 0.207190 0.597632 2 50 X_Ec2 0.149273 0.401204 3 75 X_Ec3 0.075999 0.278158 4 100 X_Ec4 0.116544 0.317179 FVA for 4 sets of fluxes/biomass at growth rate 0.730103 : No % Name Min Max 1 25 X_Ec1 0.211679 0.593976 2 50 X_Ec2 0.152032 0.394995 3 75 X_Ec3 0.077253 0.268849 4 100 X_Ec4 0.118771 0.309112 FVA for 4 sets of fluxes/biomass at growth rate 0.730839 : No % Name Min Max 1 25 X_Ec1 0.216310 0.569878 2 50 X_Ec2 0.154868 0.388666 3 75 X_Ec3 0.078538 0.259305 4 100 X_Ec4 0.127080 0.300856 FVA for 4 sets of fluxes/biomass at growth rate 0.731575 : No % Name Min Max 1 25 X_Ec1 0.221090 0.527616 2 50 X_Ec2 0.157783 0.382212 3 75 X_Ec3 0.079852 0.249515 4 100 X_Ec4 0.140974 0.292403 FVA for 4 sets of fluxes/biomass at growth rate 0.732311 : No % Name Min Max 1 25 X_Ec1 0.226026 0.484427 2 50 X_Ec2 0.160780 0.375631 3 75 X_Ec3 0.081199 0.239469 4 100 X_Ec4 0.155428 0.283745 FVA for 4 sets of fluxes/biomass at growth rate 0.733047 : No % Name Min Max 1 25 X_Ec1 0.231124 0.440276 2 50 X_Ec2 0.172784 0.368917 3 75 X_Ec3 0.082578 0.229158 4 100 X_Ec4 0.170469 0.274876 FVA for 4 sets of fluxes/biomass at growth rate 0.733783 : No % Name Min Max 1 25 X_Ec1 0.236391 0.395127 2 50 X_Ec2 0.209556 0.362068 3 75 X_Ec3 0.083992 0.218570 4 100 X_Ec4 0.186124 0.265787 FVA for 4 sets of fluxes/biomass at growth rate 0.734519 : No % Name Min Max 1 25 X_Ec1 0.241835 0.348944 2 50 X_Ec2 0.247095 0.353601 3 75 X_Ec3 0.095040 0.207693 4 100 X_Ec4 0.202424 0.256468 FVA for 4 sets of fluxes/biomass at growth rate 0.735255 : No % Name Min Max 1 25 X_Ec1 0.247466 0.301686 2 50 X_Ec2 0.285430 0.339473 3 75 X_Ec3 0.139450 0.196515 4 100 X_Ec4 0.219401 0.246911 FVA for 4 sets of fluxes/biomass at growth rate 0.735991 : No % Name Min Max 1 25 X_Ec1 0.253290 0.253311 2 50 X_Ec2 0.324588 0.324610 3 75 X_Ec3 0.185000 0.185022 4 100 X_Ec4 0.237087 0.237106
Similar to the output by fluxVariability, fvaComMin contains the minimum fluxes corresponding to the reactions in options.rxnNameList. fvaComMax contains the maximum fluxes. options.rxnNameList can be supplied as a (#rxns + #organism)-by-K matrix to analyze the variability of the K linear combinations of flux/biomass variables in the columns of the matrix. See help SteadyComFVA for more details.
We would also like to compare the results against the direct use of FBA and FVA by calling optimizeCbModel and fluxVariability:
optGRpercentFBA = [89:2:99 99.1:0.1:100]; % less dense interval to save time because the results are always the same for < 99%
nGr = numel(optGRpercentFBA);
[fvaFBAMin, fvaFBAMax] = deal(zeros(numel(options.rxnNameList), nGr));
% change the objective function to the sum of all biomass reactions
EcCom.c(:) = 0;
EcCom.c(EcCom.indCom.spBm) = 1;
EcCom.csense = char('E' * ones(1, numel(EcCom.mets)));
s = optimizeCbModel(EcCom); % run FBA
grFBA = s.f;
for jGr = 1:nGr
fprintf('Growth rate %.4f :\n', grFBA * optGRpercentFBA(jGr)/100);
[fvaFBAMin(:, jGr), fvaFBAMax(:, jGr)] = fluxVariability(EcCom, optGRpercentFBA(jGr), 'max', EcCom.infoCom.spBm, 2);
end
Growth rate 0.5091 :
No Perc Name Min Max Starting parallel pool (parpool) using the 'local' profile ... connected to 2 workers.
Growth rate 0.5205 :
No Perc Name Min Max
Growth rate 0.5319 :
No Perc Name Min Max
Growth rate 0.5434 :
No Perc Name Min Max
Growth rate 0.5548 :
No Perc Name Min Max
Growth rate 0.5663 :
No Perc Name Min Max
Growth rate 0.5668 :
No Perc Name Min Max
Growth rate 0.5674 :
No Perc Name Min Max
Growth rate 0.5680 :
No Perc Name Min Max
Growth rate 0.5686 :
No Perc Name Min Max
Growth rate 0.5691 :
No Perc Name Min Max
Growth rate 0.5697 :
No Perc Name Min Max
Growth rate 0.5703 :
No Perc Name Min Max
Growth rate 0.5708 :
No Perc Name Min Max
Growth rate 0.5714 :
No Perc Name Min Max
Growth rate 0.5720 :
No Perc Name Min Max
Plot the results to visualize the difference (see also Figure 2 in ref. [1]):
grComV = result.GRmax * options.optGRpercent / 100; % vector of growth rates tested
lgLabel = {'{\itEc1 }';'{\itEc2 }';'{\itEc3 }';'{\itEc4 }'};
col = [235 135 255; 0 235 0; 255 0 0; 95 135 255 ]/255; % color
f = figure;
% SteadyCom
subplot(2, 1, 1);
hold on
x = [grComV(:); flipud(grComV(:))];
for j = 1:4
y = [fvaComMin(j, :), fliplr(fvaComMax(j, :))];
p(j, 1) = plot(x(~isnan(y)), y(~isnan(y)), 'LineWidth', 2);
p(j, 1).Color = col(j, :);
end
tl(1) = title('\underline{SteadyCom}', 'Interpreter', 'latex');
tl(1).Position = [0.7 1.01 0];
ax(1) = gca;
ax(1).XTick = 0.66:0.02:0.74;
ax(1).YTick = 0:0.2:1;
xlim([0.66 0.74])
ylim([0 1])
 
lg = legend(lgLabel);
lg.Box = 'off';
yl(1) = ylabel('Relative abundance');
xl(1) = xlabel('Community growth rate (h^{-1})');
% FBA
grFBAV = grFBA * optGRpercentFBA / 100;
x = [grFBAV(:); flipud(grFBAV(:))];
subplot(2, 1, 2);
hold on
% plot j=1:2 only because 3:4 overlap with 1:2
for j = 1:2
y = [fvaFBAMin(j, :), fliplr(fvaFBAMax(j, :))] ./ x';
% it is possible some values > 1 because the total biomass produced is
% only bounded below when calling fluxVariability. Would be strictly
% equal to 1 if sum(biomass) = optGRpercentFBA(jGr) * grFBA is constrained. Treat them as 1.
y(y>1) = 1;
p(j, 2)= plot(x(~isnan(y)), y(~isnan(y)), 'LineWidth', 2);
p(j, 2).Color = col(j, :);
end
tl(2) = title('\underline{Joint FBA}', 'Interpreter', 'latex');
tl(2).Position = [0.55 1.01 0];
ax(2) = gca;
ax(2).XTick = 0.52:0.02:0.58;
ax(2).YTick = 0:0.2:1;
xlim([0.52 0.58])
ylim([0 1])
xl(2) = xlabel('Community growth rate (h^{-1})');
yl(2) = ylabel('Relative abundance');
ax(1).Position = [0.1 0.6 0.5 0.32];
ax(2).Position = [0.1 0.1 0.5 0.32];
lg.Position = [0.65 0.65 0.1 0.27];
The direct use of FVA compared to FVA under the SteadyCom framework gives very little information on the organism's abundance. The ranges for almost all growth rates span from 0 to 1. In contrast, SteadyComFVA returns results with the expected co-existence of all four mutants. When the growth rates get closer to the maximum, the ranges shrink to unique values.

Analyze Pairwise Relationship Using SteadyComPOA

Now we would like to see at a given growth rate, how the abundance of an organism influences the abundance of another organism. We check this by iteratively fixing the abundance of an organism at a level (independent variable) and optimizing for the maximum and minimum allowable abundance of another organism (dependent variable). This is what SteadyComPOA does.
Set up the option structure and call SteadyComPOA. Nstep is an important parameter to designate how many intermediate steps are used or which values between the min and max values of the independent variable are used for optimizing the dependent variable. savePOA options must be supplied with a non-empty string or a default name will be used for saving the POA results. By default, the function analyzes all possible pairs in options.rxnNameList. To analyze only particular pairs, use options.pairList. See help SteadyComPOA for more details.
options.savePOA = ['POA' filesep 'EcCom']; % directory and fila name for saving POA results
options.optGRpercent = [99 90 70 50]; % analyze at these percentages of max. growth rate
% Nstep is the number of intermediate steps that the independent variable will take different values
% or directly the vector of values, e.g. Nsetp = [0, 0.5, 1] implies fixing the independent variable at the minimum,
% 50% from the min to the max and the maximum value respectively to find the attainable range of the dependent variable.
% Here use small step sizes when getting close to either ends of the flux range
a = 0.001*(1000.^((0:14)/14));
options.Nstep = sort([a (1-a)]);
[POAtable, fluxRange, Stat, GRvector] = SteadyComPOA(EcCom, options);
Already finished. Results were already saved to POA/EcCom_GR0.73.mat Already finished. Results were already saved to POA/EcCom_GR0.66.mat Already finished. Results were already saved to POA/EcCom_GR0.52.mat Already finished. Results were already saved to POA/EcCom_GR0.37.mat
POAtable is a n-by-n cell if there are n targets in options.rxnNameList. POAtable{i, i} is a Nstep-by-1-by-Ngr matrix where Nstep is the number of intermediate steps detemined by options.Nstep and Ngr is the number of growth rates analyzed. POAtable{i, i}(:, :, k) is the values at which the i-th target is fixed for the community growing at the growth rate GRvector(k). POAtable{i, j} is a Nstep-by-2-by-Ngr matrix where POAtable{i, j}(:, 1, k) and POAtable{i, j}(:, 2, k) are respectively the min. and max. values of the j-th target when fixing the i-th target at the corresponding values in POAtable{i, i}(:, :, k). fluxRange contains the min. and max. values for each target (found by calling SteadyComFVA). Stat is a n-by-n-by-Ngr structure array, each containing two fields: *.cor, the correlatiion coefficient between the max/min values of the dependent variable and the independent variable, and *.r2, the R-squred of linear regression. They are also outputed in the command window during computation. All the computed results are also saved in the folder 'POA' starting with the name 'EcCom', followed by 'GRxxxx' denoting the growth rate at which the analysis is performed.
Plot the results (see also Figure 3 in ref. [1]):
nSp = 4;
spLab = {'{\it Ec1 }';'{\it Ec2 }';'{\it Ec3 }';'{\it Ec4 }'};
mark = {'A', 'B', 'D', 'C', 'E', 'F'};
nPlot = 0;
for j = 1:nSp
for k = 1:nSp
if k > j
nPlot = nPlot + 1;
ax(j, k) = subplot(nSp-1, nSp-1, (k - 2) * (nSp - 1) + j);
hold on
for p = 1:size(POAtable{1, 1}, 3)
x = [POAtable{j, j}(:, :, p);POAtable{j, j}(end:-1:1, :, p);...
POAtable{j, j}(1, 1, p)];
y = [POAtable{j, k}(:, 1, p);POAtable{j, k}(end:-1:1, 2, p);...
POAtable{j, k}(1, 1, p)];
plot(x(~isnan(y)), y(~isnan(y)), 'LineWidth', 2)
end
xlim([0.001 1])
ylim([0.001 1])
ax(j, k).XScale = 'log';
ax(j, k).YScale = 'log';
ax(j, k).XTick = [0.001 0.01 0.1 1];
ax(j, k).YTick = [0.001 0.01 0.1 1];
ax(j, k).YAxis.MinorTickValues=[];
ax(j, k).XAxis.MinorTickValues=[];
ax(j, k).TickLength = [0.03 0.01];
xlabel(spLab{j});
ylabel(spLab{k});
tx(j, k) = text(10^(-5), 10^(0.1), mark{nPlot}, 'FontSize', 12, 'FontWeight', 'bold');
end
end
end
lg = legend(strcat(strtrim(cellstr(num2str(options.optGRpercent(:)))), '%'));
lg.Position = [0.7246 0.6380 0.1700 0.2015];
lg.Box='off';
subplot(3, 3, 3, 'visible', 'off');
t = text(0.2, 0.8, {'% maximum';'growth rate'});
for j = 1:nSp
for k = 1:nSp
if k>j
ax(j, k).Position = [0.15 + (j - 1) * 0.3, 0.8 - (k - 2) * 0.3, 0.16, 0.17];
ax(j, k).Color = 'none';
end
end
end
There are two patterns observed. The two pairs showing negative correlations, namely Ec1 vs Ec4 (panel D) and Ec2 vs Ec3 (panel C) are indeed competing for the same amino acids with each other (Ec1 and Ec4 competing for Lys and Met; Ec2 and Ec4 competing for Arg and Phe). Each of the other pairs showing positive correlations are indeed the cross feeding pairs, e.g., Ec1 and Ec2 (panel A) cross feeding on Arg and Lys. See ref. [1] for more detailed discussion.

Parallelization and Timing

SteadyCom in general can be finished within 20 iterations, i.e. solving 20 LPs (usually faster if using Matlab fzero) for an accuracy of 1e-6 for the maximum community growth rate. The actual computation time depends on the size of the community metabolic network. The current EcCom model has 6971 metabolites and 9831 reactions. It took 18 seconds for a MacBook Pro with 2.5 GHz Intel Core i5, 4 GB memory running Matlab R2016b and Cplex 12.7.1.
Since the FVA and POA analysis can be time-consuming for large models with a large number of reactions to be analyzed, SteadyComFVA and SteadyComPOA support parrallelization using the Matlab Distributed Computing Toolbox (parfor for SteadyComFVA and spmd for SteadyComPOA).
Test SteadyComFVA with 2 threads:
options.rxnNameList = EcCom.rxns(1:100); % test FVA for the first 50 reactions
options.optGRpercent = 99;
options.algorithm = 1;
options.threads = 1; % test single-thread computation first
options.verbFlag = 0; % no verbose output
tic;
[minF1, maxF1] = SteadyComFVA(EcCom, options);
t1 = toc;
if isempty(gcp('nocreate'))
parpool(2); % start a parallel pool
end
Starting parallel pool (parpool) using the 'local' profile ... connected to 2 workers.
options.threads = 2; % two threads (0 to use all available workers)
tic;
[minF2, maxF2] = SteadyComFVA(EcCom, options); % test single-thread computation first
t2 = toc;
fprintf('Maximum difference between the two solutions: %.4e\n', max(max(abs(minF1 - minF2)), max(abs(maxF1 - maxF2))));
Maximum difference between the two solutions: 9.9257e-09
fprintf('\nSingle-thread computation: %.0f sec\nTwo-thread computation: %.0f sec\n', t1, t2);
Single-thread computation: 96 sec Two-thread computation: 91 sec
If there are many reactions to be analyzed, use options.saveFVA to give a relative path for saving the intermediate results. Even though the computation is interrupted, by calling SteadyComFVA with the same options.saveFVA, the program will detect previously saved results and continued from there.
Test SteadyComPOA with 2 threads:
options.rxnNameList = EcCom.rxns(find(abs(result.flux) > 1e-2, 6));
options.savePOA = 'POA/EcComParallel'; % save with a new name
options.verbFlag = 3;
options.threads = 2;
options.Nstep = 5; % use a smaller number of steps for test
tic;
[POAtable1, fluxRange1] = SteadyComPOA(EcCom, options);
Find maximum community growth rate.. Model feasible at maintenance. Time elapsed: 1 / 1 sec Iter LB To test UB Time elapsed (iteration/total) 1 0.000000 0.500000 Inf 0 / 1 sec 2 0.500000 0.721279 Inf 6 / 7 sec 3 0.721279 0.735372 Inf 0 / 7 sec 4 0.735372 0.742726 Inf 0 / 8 sec Func-count x f(x) Procedure 2 0.735372 -0.000807615 initial 3 0.735378 -0.00079987 interpolation 4 0.73599 -1.26127e-06 interpolation 5 0.73599 -1.26127e-06 interpolation Zero found in the interval [0.735372, 0.742726] Maximum community growth rate: 0.735990 (abs. error < 1e-06). Time elapsed: 26 sec FVA for 6 sets of fluxes/biomass at growth rate 0.728630 : Thread 1: 33.33% finished. 2017-07-21 13:56:18 Thread 2: 33.33% finished. 2017-07-21 13:56:18 Thread 1: 66.67% finished. 2017-07-21 13:56:20 Thread 2: 66.67% finished. 2017-07-21 13:56:20 Thread 1: 100.00% finished. 2017-07-21 13:56:21 Thread 2: 100.00% finished. 2017-07-21 13:56:21 POA for 15 pairs of reactions at growth rate 0.728630 Start from #1 Ec13HAD100 vs #2 Ec13HAD120. Rxn1 Rxn2 corMin r2 corMax r2 Time POA in parallel... Lab 2: Ec13HAD120 Ec13HAD160 0.0956 0.5000 -0.8431 0.9667 2017-07-21 13:57:45 Lab 1: Ec13HAD100 Ec13HAD120 0.5755 0.3373 0.7927 0.4005 2017-07-21 13:58:23 Lab 2: Ec13HAD121 Ec13HAD140 -0.0837 0.5000 -0.3890 0.0784 2017-07-21 13:59:32 Lab 1: Ec13HAD100 Ec13HAD121 0.2429 0.7227 0.4245 0.2168 2017-07-21 14:00:44 Lab 2: Ec13HAD121 Ec13HAD141 0.9997 1.0000 1.0000 1.0000 2017-07-21 14:01:18 Lab 1: Ec13HAD100 Ec13HAD140 -0.0915 0.4667 -0.1144 1.0000 2017-07-21 14:01:54 Lab 2: Ec13HAD121 Ec13HAD160 -0.0837 0.5000 -0.2478 0.0302 2017-07-21 14:02:33 Ec13HAD140 Ec13HAD141 -0.0197 0.1369 -0.6518 0.9578 2017-07-21 14:04:17 Lab 1: Ec13HAD100 Ec13HAD141 0.2429 0.7226 0.4245 0.2447 2017-07-21 14:04:52 Ec13HAD100 Ec13HAD160 0.0000 NaN 1.8482 0.4493 2017-07-21 14:05:44 Ec13HAD120 Ec13HAD121 -0.0922 0.3440 -0.5288 0.9995 2017-07-21 14:07:32 Lab 2: Ec13HAD140 Ec13HAD160 0.1842 0.8929 -1.0433 0.9735 2017-07-21 14:08:04 Ec13HAD141 Ec13HAD160 -0.0837 0.5000 -0.2478 0.0302 2017-07-21 14:09:11 Lab 1: Ec13HAD120 Ec13HAD140 -0.0000 NaN -1.4156 1.0000 2017-07-21 14:09:25 Lab 2: Current loop finished. Stop other workers... All workers have ceased. Redistributing... Lab 1: Ec13HAD120 Ec13HAD141 -0.0402 0.1302 -0.6122 0.9816 2017-07-21 14:10:29 Lab 2: Current loop finished. Stop other workers... All workers have ceased. Redistributing... Finished. Save final results to POA/EcComParallel_GR0.73.mat
t3 = toc;
The parallelization code uses spmd and will redistribute jobs once any of the workers has finished to maximize the computational efficiency.
options.savePOA = 'POA/EcComSingeThread';
options.threads = 1;
tic;
[POAtable2, fluxRange2] = SteadyComPOA(EcCom, options);
Find maximum community growth rate.. Model feasible at maintenance. Time elapsed: 1 / 1 sec Iter LB To test UB Time elapsed (iteration/total) 1 0.000000 0.500000 Inf 0 / 1 sec 2 0.500000 0.721279 Inf 5 / 6 sec 3 0.721279 0.735372 Inf 0 / 6 sec 4 0.735372 0.742726 Inf 0 / 6 sec Func-count x f(x) Procedure 2 0.735372 -0.000807615 initial 3 0.735378 -0.00079987 interpolation 4 0.73599 -1.26127e-06 interpolation 5 0.73599 -1.26127e-06 interpolation Zero found in the interval [0.735372, 0.742726] Maximum community growth rate: 0.735990 (abs. error < 1e-06). Time elapsed: 24 sec FVA for 6 sets of fluxes/biomass at growth rate 0.728630 : No % Name Min Max 1 17 Ec13HAD100 0.052591 0.217439 2 33 Ec13HAD120 0.000000 0.262936 3 50 Ec13HAD121 0.022231 0.202541 4 67 Ec13HAD140 0.000000 0.243774 5 83 Ec13HAD141 0.022231 0.202541 6 100 Ec13HAD160 0.000000 0.251518 POA for 15 pairs of reactions at growth rate 0.728630 Start from #1 Ec13HAD100 vs #2 Ec13HAD120. Rxn1 Rxn2 corMin r2 corMax r2 Time Ec13HAD100 Ec13HAD120 0.5755 0.3373 0.7927 0.4005 2017-07-21 14:11:54 Ec13HAD100 Ec13HAD121 0.2429 0.7227 0.4245 0.2168 2017-07-21 14:13:16 Ec13HAD100 Ec13HAD140 -0.0915 0.4667 -0.1144 1.0000 2017-07-21 14:13:54 Ec13HAD100 Ec13HAD141 0.2429 0.7226 0.4245 0.2447 2017-07-21 14:15:10 Ec13HAD100 Ec13HAD160 0.0000 NaN 1.8482 0.4493 2017-07-21 14:15:39 Ec13HAD120 Ec13HAD121 -0.0922 0.3440 -0.5288 0.9995 2017-07-21 14:16:30 Ec13HAD120 Ec13HAD140 -0.0000 NaN -1.4156 1.0000 2017-07-21 14:17:36 Ec13HAD120 Ec13HAD141 0.0637 1.0000 -0.6611 0.9793 2017-07-21 14:18:38 Ec13HAD120 Ec13HAD160 0.1435 0.6000 -0.8448 0.9673 2017-07-21 14:18:48 Ec13HAD121 Ec13HAD140 -0.0837 0.5000 -0.3890 0.0784 2017-07-21 14:19:34 Ec13HAD121 Ec13HAD141 0.9997 1.0000 1.0000 1.0000 2017-07-21 14:20:18 Ec13HAD121 Ec13HAD160 -0.0837 0.5000 -0.2478 0.0302 2017-07-21 14:20:44 Ec13HAD140 Ec13HAD141 -0.0026 0.0014 -0.6518 0.9589 2017-07-21 14:21:16 Ec13HAD140 Ec13HAD160 0.1547 0.6000 -0.9028 1.0000 2017-07-21 14:22:06 Ec13HAD141 Ec13HAD160 -0.0837 0.4667 -0.2437 0.0293 2017-07-21 14:22:51 Finished. Save final results to POA/EcComSingeThread_GR0.73.mat
t4 = toc;
dev = 0;
for i = 1:size(POAtable1, 1)
for j = i:size(POAtable1, 2)
dev = max(max(max(abs(POAtable1{i, j} - POAtable2{i, j}))));
dev = max(dev, max(max(abs(fluxRange1 - fluxRange2))));
end
end
fprintf('Maximum difference between the two solutions: %.4e\n', dev);
Maximum difference between the two solutions: 1.7043e-09
fprintf('\nSingle-thread computation: %.0f sec\nTwo-thread computation: %.0f sec\n', t4, t3);
Single-thread computation: 742 sec Two-thread computation: 879 sec
The advantage will be more significant for more targets to analyzed and more threads used. Similar to SteadyComFVA, SteadyComPOA also supports continuation from previously interrupted computation by calling with the same options.savePOA.

REFERENCES

[1] Chan SHJ, Simons MN, Maranas CD (2017) SteadyCom: Predicting microbial abundances while ensuring community stability. PLoS Comput Biol 13(5): e1005539. https://doi.org/10.1371/journal.pcbi.1005539
[2] Khandelwal RA, Olivier BG, Röling WFM, Teusink B, Bruggeman FJ (2013) Community Flux Balance Analysis for Microbial Consortia at Balanced Growth. PLoS ONE 8(5): e64567. https://doi.org/10.1371/journal.pone.0064567