def find_unconserved_metabolites(model): """ Detect unconserved metabolites. Parameters ---------- model : cobra.Model The metabolic model under investigation. Notes ----- See [1]_ section 3.2 for a complete description of the algorithm. .. [1] Gevorgyan, A., M. G Poolman, and D. A Fell. "Detection of Stoichiometric Inconsistencies in Biomolecular Models." Bioinformatics 24, no. 19 (2008): 2245. """ Model, Constraint, Variable, Objective = con_helpers.get_interface(model) stoich_trans = Model() internal_rxns = con_helpers.get_internals(model) metabolites = set(met for rxn in internal_rxns for met in rxn.metabolites) # The binary variables k[i] in the paper. k_vars = list() for met in metabolites: # The element m[i] of the mass vector. m_var = Variable(met.id) k_var = Variable("k_{}".format(met.id), type="binary") k_vars.append(k_var) stoich_trans.add([m_var, k_var]) # This constraint is equivalent to 0 <= k[i] <= m[i]. stoich_trans.add( Constraint(k_var - m_var, ub=0, name="switch_{}".format(met.id))) stoich_trans.update() con_helpers.add_reaction_constraints(stoich_trans, internal_rxns, Constraint) # The objective is to maximize the binary indicators k[i], subject to the # above inequality constraints. stoich_trans.objective = Objective(1) stoich_trans.objective.set_linear_coefficients({var: 1. for var in k_vars}) stoich_trans.objective.direction = "max" status = stoich_trans.optimize() if status == "optimal": # TODO: See if that could be a Boolean test `bool(var.primal)`. return set([ model.metabolites.get_by_id(var.name[2:]) for var in k_vars if var.primal < 0.8 ]) else: raise RuntimeError( "Could not compute list of unconserved metabolites." " Solver status is '{}'" " (only optimal or infeasible expected).".format(status))
def check_stoichiometric_consistency(model): """ Verify the consistency of the model stoichiometry. Parameters ---------- model : cobra.Model The metabolic model under investigation. Notes ----- See [1]_ section 3.1 for a complete description of the algorithm. .. [1] Gevorgyan, A., M. G Poolman, and D. A Fell. "Detection of Stoichiometric Inconsistencies in Biomolecular Models." Bioinformatics 24, no. 19 (2008): 2245. """ Model, Constraint, Variable, Objective = con_helpers.get_interface(model) # The transpose of the stoichiometric matrix N.T in the paper. stoich_trans = Model() internal_rxns = con_helpers.get_internals(model) metabolites = set(met for rxn in internal_rxns for met in rxn.metabolites) LOGGER.info("model '%s' has %d internal reactions", model.id, len(internal_rxns)) LOGGER.info("model '%s' has %d internal metabolites", model.id, len(metabolites)) for metabolite in metabolites: stoich_trans.add(Variable(metabolite.id, lb=1)) stoich_trans.update() con_helpers.add_reaction_constraints(stoich_trans, internal_rxns, Constraint) # The objective is to minimize the metabolite mass vector. stoich_trans.objective = Objective(1) stoich_trans.objective.set_linear_coefficients( {var: 1. for var in stoich_trans.variables}) stoich_trans.objective.direction = "min" status = stoich_trans.optimize() if status == "optimal": return True elif status == "infeasible": return False else: raise RuntimeError( "Could not determine stoichiometric consistencty." " Solver status is '{}'" " (only optimal or infeasible expected).".format(status))
def find_inconsistent_min_stoichiometry(model, atol=1e-13): """ Detect inconsistent minimal net stoichiometries. Parameters ---------- model : cobra.Model The metabolic model under investigation. atol : float, optional Values below the absolute tolerance are treated as zero. Expected to be very small but larger than zero. Notes ----- See [1]_ section 3.3 for a complete description of the algorithm. References ---------- .. [1] Gevorgyan, A., M. G Poolman, and D. A Fell. "Detection of Stoichiometric Inconsistencies in Biomolecular Models." Bioinformatics 24, no. 19 (2008): 2245. """ if check_stoichiometric_consistency(model): return set() Model, Constraint, Variable, Objective = con_helpers.get_interface(model) unconserved_mets = find_unconserved_metabolites(model) LOGGER.info("model has %d unconserved metabolites", len(unconserved_mets)) internal_rxns = con_helpers.get_internals(model) internal_mets = set(met for rxn in internal_rxns for met in rxn.metabolites) get_id = attrgetter("id") reactions = sorted(internal_rxns, key=get_id) metabolites = sorted(internal_mets, key=get_id) stoich, met_index, rxn_index = con_helpers.stoichiometry_matrix( metabolites, reactions) left_ns = con_helpers.nullspace(stoich.T) # deal with numerical instabilities left_ns[np.abs(left_ns) < atol] = 0.0 LOGGER.info("nullspace has dimension %d", left_ns.shape[1]) inc_minimal = set() (problem, indicators) = con_helpers.create_milp_problem(left_ns, metabolites, Model, Variable, Constraint, Objective) LOGGER.debug(str(problem)) cuts = list() for met in unconserved_mets: row = met_index[met] if (left_ns[row] == 0.0).all(): LOGGER.debug("%s: singleton minimal unconservable set", met.id) # singleton set! inc_minimal.add((met, )) continue # expect a positive mass for the unconserved metabolite problem.variables[met.id].lb = 1e-3 status = problem.optimize() while status == "optimal": LOGGER.debug("%s: status %s", met.id, status) LOGGER.debug("sum of all primal values: %f", sum(problem.primal_values.values())) LOGGER.debug("sum of binary indicators: %f", sum(var.primal for var in indicators)) solution = [ model.metabolites.get_by_id(var.name[2:]) for var in indicators if var.primal > 0.2 ] LOGGER.debug("%s: set size %d", met.id, len(solution)) inc_minimal.add(tuple(solution)) if len(solution) == 1: break cuts.append( con_helpers.add_cut(problem, indicators, len(solution) - 1, Constraint)) status = problem.optimize() LOGGER.debug("%s: last status %s", met.id, status) # reset problem.variables[met.id].lb = 0.0 problem.remove(cuts) cuts.clear() return inc_minimal