Skip to content

Commit

Permalink
Merge pull request #1425 from lvalcarcel/ROOM_20190213
Browse files Browse the repository at this point in the history
ROOM implementation (Regulatory on/off minimization of  metabolic flux changes)
  • Loading branch information
laurentheirendt authored Mar 15, 2019
2 parents 2ff374a + 18f9051 commit fc89122
Show file tree
Hide file tree
Showing 10 changed files with 438 additions and 94 deletions.
9 changes: 9 additions & 0 deletions docs/source/modules/analysis/ROOM/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

.. _ROOM:


ROOM
----

.. automodule:: src.analysis.ROOM
:members:
1 change: 1 addition & 0 deletions docs/source/modules/analysis/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Analysis
rFBA/index
rMTA/index
robustnessAnalysis/index
ROOM/index
rumba/index
sampling/index
sparseFBA/index
Expand Down
130 changes: 130 additions & 0 deletions src/analysis/ROOM/ROOM.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
function [fluxROOM, solutionROOM, totalFluxDiff] = ROOM(model, fluxWT, rxnKO, varargin)
% Performs a MILP version of the ROOM (Regulatory on/off minimization of
% metabolic flux changes) approach
%
% USAGE:
%
% [fluxROOM, solutionROOM, totalFluxDiff] = ROOM(model, WTflux, rxnKO, delta, epsilon, printLevel)
%
% INPUTS:
% model: Metabolic model
% fluxWT: Numeric array with flux distribution of wild type
% rxnKO: List of perturbations performed to the model
% (reactions that are eliminated)
%
% OPTIONAL INPUTS:
% delta: Multiplicative tol for flux change (Default = 0.03)
% epsilon: Additive tolerance for flux change (Default = 0.001)
% printLevel: Verbose output (Default = 1)
%
% OUTPUTS:
% fluxROOM: Flux distribution after ROOM calculation
% solutionROOM: Solution structure
% totalFluxDiff: Euclidean distance of ROOM objective, i.e.
% :math:`\sum (v_{wt}-v_{del})^2`
%
% Solve the following problem:
%
% .. math::
% min ~&~ \sum y_{i} \\
% ~&~ S_{del}v_{del} = 0 \\
% ~&~ lb_{del} \leq v_{del} \leq ub_{del} \\
% ~&~ for i=1:nRxns\\
% ~&~ v_{i} - y_{i}(v_{max,i}-w_{wt,i}^u) \leq w_{wt,i}^u \\
% ~&~ v_{i} - y_{i}(v_{min,i}-w_{wt,i}^l) \geq w_{wt,i}^l \\
% ~&~ y_{i} \in {0,1} \\
% ~&~ w_{wt,i}^u = w_{wt,i} + \delta |w_{wt,i}| + \epsilon \\
% ~&~ w_{wt,i}^l = w_{wt,i} - \delta |w_{wt,i}| - \epsilon \\
%
% NOTE::
%
% The code here has been based on:
% Shlomi, T., Berkman, O., & Ruppin, E. (2005). Regulatory on/off
% minimization of metabolic flux changes after genetic perturbations.
% Proceedings of the National Academy of Sciences, 102(21), 7695-7700
%
% .. Authors:
% - Luis V. Valcarcel, 23/01/2019, University of Navarra, CIMA & TECNUN School of Engineering.


p = inputParser;
% check required arguments
addRequired(p, 'model');
addRequired(p, 'WTflux', @(x)isnumeric(x)&&isvector(x));
addRequired(p, 'rxnKO', @(x)iscell(x));
% Check optional arguments
addParameter(p, 'delta', 0.03, @(x)isnumeric(x)&&isscalar(x));
addParameter(p, 'epsilon', 0.001, @(x)isnumeric(x)&&isscalar(x));
addParameter(p, 'printLevel', 1, @(x)isnumeric(x)&&isscalar(x));
% extract variables from parser
parse(p, model, fluxWT, rxnKO, varargin{:});
delta = p.Results.delta;
epsilon = p.Results.epsilon;
printLevel = p.Results.printLevel;

% LP solution tolerance
global CBT_LP_PARAMS
if (exist('CBT_LP_PARAMS', 'var'))
if isfield(CBT_LP_PARAMS, 'objTol')
tol = CBT_LP_PARAMS.objTol;
else
tol = 1e-6;
end
else
tol = 1e-6;
end


% Check the inputs
fluxWT = reshape(fluxWT,[],1); % reshape flux vector
assert(numel(model.rxns)==numel(fluxWT), 'This flux distribution has different number of reactions than the model')
assert(norm(model.S * fluxWT)< 10*tol, 'This flux distribution cannot exist in this model')
assert(all(ismember(rxnKO, model.rxns)), 'Some reactions are not in the model')

% Eliminate almost-zero fluxes
fluxWT(abs(fluxWT)<tol) = 0;

% generate auxiliary variables
WT_upperTol = fluxWT + delta*abs(fluxWT) + epsilon;
WT_lowerTol = fluxWT - delta*abs(fluxWT) - epsilon;

%auxiliary variables
[nMets, nRxns] = size(model.S);

% Generate model
ROOMmodel = model;
new_variables = strcat('binary_',model.rxns);
% add novel variables
ROOMmodel = addCOBRAVariables(ROOMmodel,new_variables,'lb',0,'ub',1);
% add novel constrains
for i=1:nRxns
ROOMmodel = addCOBRAConstraints(ROOMmodel, {model.rxns{i}, strcat('binary_',model.rxns{i})}, WT_upperTol(i),...
'c', [+1, -(model.ub(i)-WT_upperTol(i))], 'dsense', 'L');
end
for i=1:nRxns
ROOMmodel = addCOBRAConstraints(ROOMmodel, {model.rxns{i}, strcat('binary_',model.rxns{i})}, WT_lowerTol(i),...
'c', [+1, -(model.lb(i)-WT_lowerTol(i))], 'dsense', 'G');
end
% Block reactions in model
ROOMmodel = changeRxnBounds(ROOMmodel, rxnKO, 0, 'b');
% change objective function
ROOMmodel.c(:) = 0;
ROOMmodel.evarc(cellfun(@length,regexp(ROOMmodel.evars,'^binary_'))==1) = 1;
ROOMmodel.osenseStr = 'min';

MILPproblem = buildLPproblemFromModel(ROOMmodel);

MILPproblem.vartype = char(ones(1,size(MILPproblem.A,2))*'C');
MILPproblem.vartype(MILPproblem.c==1) = 'B';

solutionROOM = solveCobraMILP(MILPproblem, 'printLevel', printLevel);

if solutionROOM.stat == 1
fluxROOM = solutionROOM.full(1:nRxns);
totalFluxDiff = norm(fluxROOM-fluxWT);
else
fluxROOM = nan(nRxns,1);
totalFluxDiff = nan;
end

end
125 changes: 125 additions & 0 deletions src/analysis/ROOM/linearROOM.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
function [fluxROOM, solutionROOM, totalFluxDiff] = linearROOM(model, fluxWT, rxnKO, varargin)
% Performs a LP version of the ROOM (Regulatory on/off minimization of
% metabolic flux changes) approach
%
% USAGE:
%
% [fluxROOM, solutionROOM, totalFluxDiff] = linearROOM(model, WTflux, rxnKO, delta, epsilon, printLevel)
%
% INPUTS:
% model: Metabolic model
% fluxWT: Numeric array with flux distribution of wild type
% rxnKO: List of perturbations performed to the model
% (reactions that are eliminated)
%
% OPTIONAL INPUTS:
% delta: Multiplicative tol for flux change (Default = 0.03)
% epsilon: Additive tolerance for flux change (Default = 0.001)
% printLevel: Verbose output (Default = 1)
%
% OUTPUTS:
% fluxROOM: Flux distribution after ROOM calculation
% solutionROOM: Solution structure
% totalFluxDiff: Euclidean distance of ROOM objective, i.e.
% :math:`\sum (v_{wt}-v_{del})^2`
%
% Solve the following problem:
%
% .. math::
% min ~&~ \sum y_{i} \\
% ~&~ S_{del}v_{del} = 0 \\
% ~&~ lb_{del} \leq v_{del} \leq ub_{del} \\
% ~&~ for i=1:nRxns\\
% ~&~ v_{i} - y_{i}(v_{max,i}-w_{wt,i}^u) \leq w_{wt,i}^u \\
% ~&~ v_{i} - y_{i}(v_{min,i}-w_{wt,i}^l) \geq w_{wt,i}^l \\
% ~&~ 0 \leq y_{i} \leq 1 \\
% ~&~ w_{wt,i}^u = w_{wt,i} + \delta |w_{wt,i}| + \epsilon \\
% ~&~ w_{wt,i}^l = w_{wt,i} - \delta |w_{wt,i}| - \epsilon \\
%
% NOTE::
%
% The code here has been based on:
% Shlomi, T., Berkman, O., & Ruppin, E. (2005). Regulatory on/off
% minimization of metabolic flux changes after genetic perturbations.
% Proceedings of the National Academy of Sciences, 102(21), 7695-7700
%
% .. Authors:
% - Luis V. Valcarcel, 23/01/2019, University of Navarra, CIMA & TECNUN School of Engineering.


p = inputParser;
% check required arguments
addRequired(p, 'model');
addRequired(p, 'WTflux', @(x)isnumeric(x)&&isvector(x));
addRequired(p, 'rxnKO', @(x)iscell(x));
% Check optional arguments
addParameter(p, 'delta', 0.03, @(x)isnumeric(x)&&isscalar(x));
addParameter(p, 'epsilon', 0.001, @(x)isnumeric(x)&&isscalar(x));
addParameter(p, 'printLevel', 1, @(x)isnumeric(x)&&isscalar(x));
% extract variables from parser
parse(p, model, fluxWT, rxnKO, varargin{:});
delta = p.Results.delta;
epsilon = p.Results.epsilon;
printLevel = p.Results.printLevel;

% LP solution tolerance
global CBT_LP_PARAMS
if (exist('CBT_LP_PARAMS', 'var'))
if isfield(CBT_LP_PARAMS, 'objTol')
tol = CBT_LP_PARAMS.objTol;
else
tol = 1e-6;
end
else
tol = 1e-6;
end


% Check the inputs
fluxWT = reshape(fluxWT,[],1); % reshape flux vector
assert(numel(model.rxns)==numel(fluxWT), 'This flux distribution has different number of reactions than the model')
assert(norm(model.S * fluxWT)< 10*tol, 'This flux distribution cannot exist in this model')
assert(all(ismember(rxnKO, model.rxns)), 'Some reactions are not in the model')

% Eliminate almost-zero fluxes
fluxWT(abs(fluxWT)<tol) = 0;

% generate auxiliary variables
WT_upperTol = fluxWT + delta*abs(fluxWT) + epsilon;
WT_lowerTol = fluxWT - delta*abs(fluxWT) - epsilon;

%auxiliary variables
[nMets, nRxns] = size(model.S);

% Generate model
ROOMmodel = model;
new_variables = strcat('binary_',model.rxns);
% add novel variables
ROOMmodel = addCOBRAVariables(ROOMmodel,new_variables,'lb',0,'ub',1);
% add novel constrains
for i=1:nRxns
ROOMmodel = addCOBRAConstraints(ROOMmodel, {model.rxns{i}, strcat('binary_',model.rxns{i})}, WT_upperTol(i),...
'c', [+1, -(model.ub(i)-WT_upperTol(i))], 'dsense', 'L');
end
for i=1:nRxns
ROOMmodel = addCOBRAConstraints(ROOMmodel, {model.rxns{i}, strcat('binary_',model.rxns{i})}, WT_lowerTol(i),...
'c', [+1, -(model.lb(i)-WT_lowerTol(i))], 'dsense', 'G');
end
% Block reactions in model
ROOMmodel = changeRxnBounds(ROOMmodel, rxnKO, 0, 'b');
% change objective function
ROOMmodel.c(:) = 0;
ROOMmodel.evarc(cellfun(@length,regexp(ROOMmodel.evars,'^binary_'))==1) = 1;
ROOMmodel.osenseStr = 'min';

solutionROOM = optimizeCbModel(ROOMmodel);

if solutionROOM.stat == 1
fluxROOM = solutionROOM.full(1:nRxns);
totalFluxDiff = norm(fluxROOM-fluxWT);
else
fluxROOM = nan(nRxns,1);
totalFluxDiff = nan;
end

end
30 changes: 15 additions & 15 deletions src/analysis/gMCS/GPR2models.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
% * 1 - sequential
% * 2+ - parallel
% printLevel: show the reactions created in models.
% * 0 - shows nothing
% * 0 - shows nothing
% * 1 - shows progress by reactions (default)
% * 2+ - shows everything (reaction and network generation)
%
Expand All @@ -31,10 +31,10 @@
% rule for each reaction.
%
% .. Authors:
% - Iñigo Apaolaza, Aug 2017, University of Navarra, TECNUN School of Engineering.
% - Inigo Apaolaza, Aug 2017, University of Navarra, TECNUN School of Engineering.
% - Luis V. Valcarcel, Aug 2017, University of Navarra, TECNUN School of Engineering.
% - Francisco J. Planes, Aug 2017, University of Navarra, TECNUN School of Engineering.
% - Iñigo Apaolaza, April 2018, University of Navarra, TECNUN School of Engineering.
% - Inigo Apaolaza, April 2018, University of Navarra, TECNUN School of Engineering.

if (nargin < 5 || isempty(printLevel))
printLevel = 1; % Default is show progress
Expand Down Expand Up @@ -100,13 +100,13 @@
% include metabolic reaction as objective metabolite
model.mets{1} = RXN;
% Add objective function
model.rxns{1} = RXN;
model.rxns{1} = RXN;
model.S(1,1) = -1;

% Check if there is any gene related to this reaction and add them
genes = metabolic_model.genes(metabolic_model.rxnGeneMat(selected_rxns(i),:)>0);
if length(genes)>1
model.mets = vertcat(RXN, genes); % Include genes in the model
model.mets = vertcat(RXN, genes); % Include genes in the model
% model = addMetabolite(model,genes);
model.genes = genes;
fp = FormulaParser();
Expand All @@ -130,12 +130,12 @@
model.rxns(demand_idx) = strcat('DM_',genes);
model.rxns = model.rxns';
model.S(gene_idx,demand_idx) = eye(length(genes));

% converge isoforms (if neccesary)
if ~isempty(separate_transcript) && length(genes)>1
model = convergeTranscripts2Gene(model, separate_transcript);
end

% Generate rest of fields
idx_rxns = (1:length(model.rxns))';
idx_mets = (1:length(model.mets))';
Expand All @@ -152,7 +152,7 @@
model.lb = model.lb';
model.ub = model.ub';
model.rules = model.rules';

% Store the model
networks{i} = model;
end
Expand All @@ -177,7 +177,7 @@
% g1 & g2 & g3 is one layer in the model, RXN = g1 + g2 + g3
% (g1 | g2) & g3 is a two layer model: RXN = (g1 | g2) + g3
% (g1 | g2) = g1
% (g1 | g2) = g2
% (g1 | g2) = g2



Expand Down Expand Up @@ -241,7 +241,7 @@
[~, rxn_idx] = find((model.S(node_idx(i),:)<0));
rxn_name = model.rxns(rxn_idx);
% in this reaction, change (node -> rule) for (gene -> rule)
model = changeRxnMets(model, node_name, genes(gen), rxn_name);
model = changeRxnMets(model, node_name, genes(gen), rxn_name);
end
% remove stoichimetri of intermediate nodes
model.S(node_idx,:)=0;
Expand All @@ -263,10 +263,10 @@
% Some COBRA models have transcripts instead of genes. Most of the
% transcripts have the same functionallity, so we can converge them as the
% same gene.
% Ej: gene 10005.1
% gene 10005.2 ==> gene 10005
% Ej: gene 10005.1
% gene 10005.2 ==> gene 10005
% gene 10005.3
%
%
% This is done for every gene of the model at exchange reaction level

% seach demand reactions
Expand Down Expand Up @@ -313,4 +313,4 @@
model.S(gene_idx,demand_idx) = eye(length(genes_unique));


end
end
Loading

0 comments on commit fc89122

Please sign in to comment.