def find_exchange_rxns(model): u""" Return a list of exchange reactions. Parameters ---------- model : cobra.Model The metabolic model under investigation. Notes ----- [1] defines exchange reactions as: -- reactions that 'define the extracellular environment' -- 'unbalanced, extra-organism reactions that represent the supply to or removal of metabolites from the extra-organism "space"' -- reactions with a formula such as: 'met_e -> ' or ' -> met_e' or 'met_e <=> ' Exchange reactions differ from demand reactions in that the metabolites are removed from or added to the extracellular environment only. With this the uptake or secretion of a metabolite is modeled, respectively. References ---------- .. [1] Thiele, I., & Palsson, B. Ø. (2010, January). A protocol for generating a high-quality genome-scale metabolic reconstruction. Nature protocols. Nature Publishing Group. http://doi.org/10.1038/nprot.2009.203 """ try: extracellular = find_compartment_id_in_model(model, 'e') except KeyError: extracellular = None return find_boundary_types(model, 'exchange', extracellular)
def find_exchange_rxns(model): u""" Return a list of exchange reactions. Parameters ---------- model : cobra.Model The metabolic model under investigation. Notes ----- [1] defines exchange reactions as: -- reactions that 'define the extracellular environment' -- 'unbalanced, extra-organism reactions that represent the supply to or removal of metabolites from the extra-organism "space"' -- reactions with a formula such as: 'met_e -> ' or ' -> met_e' or 'met_e <=> ' Exchange reactions differ from demand reactions in that the metabolites are removed from or added to the extracellular environment only. With this the uptake or secretion of a metabolite is modeled, respectively. References ---------- .. [1] Thiele, I., & Palsson, B. Ø. (2010, January). A protocol for generating a high-quality genome-scale metabolic reconstruction. Nature protocols. Nature Publishing Group. http://doi.org/10.1038/nprot.2009.203 """ try: extracellular = find_compartment_id_in_model(model, 'e') except KeyError: extracellular = None return find_boundary_types(model, 'exchange', extracellular)
def add_mip_obj(model): """Add a mixed-integer version of a minimal medium to the model. Changes the optimization objective to finding the medium with the least components:: minimize size(R) where R part of import_reactions Arguments --------- model : cobra.model The model to modify. """ if len(model.variables) > 1e4: LOGGER.warning("the MIP version of minimal media is extremely slow for" " models that large :(") exchange_rxns = find_boundary_types(model, "exchange") big_m = max(abs(b) for r in exchange_rxns for b in r.bounds) prob = model.problem coefs = {} to_add = [] for rxn in exchange_rxns: export = len(rxn.reactants) == 1 indicator = prob.Variable("ind_" + rxn.id, lb=0, ub=1, type="binary") if export: vrv = rxn.reverse_variable indicator_const = prob.Constraint(vrv - indicator * big_m, ub=0, name="ind_constraint_" + rxn.id) else: vfw = rxn.forward_variable indicator_const = prob.Constraint(vfw - indicator * big_m, ub=0, name="ind_constraint_" + rxn.id) to_add.extend([indicator, indicator_const]) coefs[indicator] = 1 model.add_cons_vars(to_add) model.solver.update() model.objective.set_linear_coefficients(coefs) model.objective.direction = "min"
def add_linear_obj(model): """Add a linear version of a minimal medium to the model solver. Changes the optimization objective to finding the growth medium requiring the smallest total import flux:: minimize sum |r_i| for r_i in import_reactions Arguments --------- model : cobra.Model The model to modify. """ coefs = {} for rxn in find_boundary_types(model, "exchange"): export = len(rxn.reactants) == 1 if export: coefs[rxn.reverse_variable] = 1 else: coefs[rxn.forward_variable] = 1 model.objective.set_linear_coefficients(coefs) model.objective.direction = "min"
def add_linear_obj(model): """Add a linear version of a minimal medium to the model solver. Changes the optimization objective to finding the growth medium requiring the smallest total import flux:: minimize sum |r_i| for r_i in import_reactions Arguments --------- model : cobra.Model The model to modify. """ coefs = {} for rxn in find_boundary_types(model, "exchange"): export = len(rxn.reactants) == 1 if export: coefs[rxn.reverse_variable] = 1 else: coefs[rxn.forward_variable] = 1 model.objective.set_linear_coefficients(coefs) model.objective.direction = "min"
def add_mip_obj(model): """Add a mixed-integer version of a minimal medium to the model. Changes the optimization objective to finding the medium with the least components:: minimize size(R) where R part of import_reactions Arguments --------- model : cobra.model The model to modify. """ if len(model.variables) > 1e4: LOGGER.warning("the MIP version of minimal media is extremely slow for" " models that large :(") exchange_rxns = find_boundary_types(model, "exchange") big_m = max(abs(b) for r in exchange_rxns for b in r.bounds) prob = model.problem coefs = {} to_add = [] for rxn in exchange_rxns: export = len(rxn.reactants) == 1 indicator = prob.Variable("ind_" + rxn.id, lb=0, ub=1, type="binary") if export: vrv = rxn.reverse_variable indicator_const = prob.Constraint( vrv - indicator * big_m, ub=0, name="ind_constraint_" + rxn.id) else: vfw = rxn.forward_variable indicator_const = prob.Constraint( vfw - indicator * big_m, ub=0, name="ind_constraint_" + rxn.id) to_add.extend([indicator, indicator_const]) coefs[indicator] = 1 model.add_cons_vars(to_add) model.solver.update() model.objective.set_linear_coefficients(coefs) model.objective.direction = "min"
def find_demand_reactions(model): u""" Return a list of demand reactions. Parameters ---------- model : cobra.Model The metabolic model under investigation. Notes ----- [1] defines demand reactions as: -- 'unbalanced network reactions that allow the accumulation of a compound' -- reactions that are chiefly added during the gap-filling process -- as a means of dealing with 'compounds that are known to be produced by the organism [..] (i) for which no information is available about their fractional distribution to the biomass or (ii) which may only be produced in some environmental conditions -- reactions with a formula such as: 'met_c -> ' Demand reactions differ from exchange reactions in that the metabolites are not removed from the extracellular environment, but from any of the organism's compartments. References ---------- .. [1] Thiele, I., & Palsson, B. Ø. (2010, January). A protocol for generating a high-quality genome-scale metabolic reconstruction. Nature protocols. Nature Publishing Group. http://doi.org/10.1038/nprot.2009.203 """ try: extracellular = find_compartment_id_in_model(model, 'e') except KeyError: extracellular = None return find_boundary_types(model, 'demand', extracellular)
def find_demand_reactions(model): u""" Return a list of demand reactions. Parameters ---------- model : cobra.Model The metabolic model under investigation. Notes ----- [1] defines demand reactions as: -- 'unbalanced network reactions that allow the accumulation of a compound' -- reactions that are chiefly added during the gap-filling process -- as a means of dealing with 'compounds that are known to be produced by the organism [..] (i) for which no information is available about their fractional distribution to the biomass or (ii) which may only be produced in some environmental conditions -- reactions with a formula such as: 'met_c -> ' Demand reactions differ from exchange reactions in that the metabolites are not removed from the extracellular environment, but from any of the organism's compartments. References ---------- .. [1] Thiele, I., & Palsson, B. Ø. (2010, January). A protocol for generating a high-quality genome-scale metabolic reconstruction. Nature protocols. Nature Publishing Group. http://doi.org/10.1038/nprot.2009.203 """ try: extracellular = find_compartment_id_in_model(model, 'e') except KeyError: extracellular = None return find_boundary_types(model, 'demand', extracellular)
def find_sink_reactions(model): u""" Return a list of sink reactions. Parameters ---------- model : cobra.Model The metabolic model under investigation. Notes ----- [1] defines sink reactions as: -- 'similar to demand reactions' but reversible, thus able to supply the model with metabolites -- reactions that are chiefly added during the gap-filling process -- as a means of dealing with 'compounds that are produced by nonmetabolic cellular processes but that need to be metabolized' -- reactions with a formula such as: 'met_c <-> ' Sink reactions differ from exchange reactions in that the metabolites are not removed from the extracellular environment, but from any of the organism's compartments. References ---------- .. [1] Thiele, I., & Palsson, B. Ø. (2010, January). A protocol for generating a high-quality genome-scale metabolic reconstruction. Nature protocols. Nature Publishing Group. http://doi.org/10.1038/nprot.2009.203 """ try: extracellular = find_compartment_id_in_model(model, 'e') except KeyError: extracellular = None return find_boundary_types(model, 'sink', extracellular)
def find_sink_reactions(model): u""" Return a list of sink reactions. Parameters ---------- model : cobra.Model The metabolic model under investigation. Notes ----- [1] defines sink reactions as: -- 'similar to demand reactions' but reversible, thus able to supply the model with metabolites -- reactions that are chiefly added during the gap-filling process -- as a means of dealing with 'compounds that are produced by nonmetabolic cellular processes but that need to be metabolized' -- reactions with a formula such as: 'met_c <-> ' Sink reactions differ from exchange reactions in that the metabolites are not removed from the extracellular environment, but from any of the organism's compartments. References ---------- .. [1] Thiele, I., & Palsson, B. Ø. (2010, January). A protocol for generating a high-quality genome-scale metabolic reconstruction. Nature protocols. Nature Publishing Group. http://doi.org/10.1038/nprot.2009.203 """ try: extracellular = find_compartment_id_in_model(model, 'e') except KeyError: extracellular = None return find_boundary_types(model, 'sink', extracellular)
def minimal_medium(model, min_objective_value=0.1, exports=False, minimize_components=False, open_exchanges=False): """ Find the minimal growth medium for the model. Finds the minimal growth medium for the model which allows for model as well as individual growth. Here, a minimal medium can either be the medium requiring the smallest total import flux or the medium requiring the least components (ergo ingredients), which will be much slower due to being a mixed integer problem (MIP). Arguments --------- model : cobra.model The model to modify. min_objective_value : positive float or array-like object The minimum growth rate (objective) that has to be achieved. exports : boolean Whether to include export fluxes in the returned medium. Defaults to False which will only return import fluxes. minimize_components : boolean or positive int Whether to minimize the number of components instead of the total import flux. Might be more intuitive if set to True but may also be slow to calculate for large communities. If set to a number `n` will return up to `n` alternative solutions all with the same number of components. open_exchanges : boolean or number Whether to ignore currently set bounds and make all exchange reactions in the model possible. If set to a number all exchange reactions will be opened with (-number, number) as bounds. Returns ------- pandas.Series, pandas.DataFrame or None A series giving the import flux for each required import reaction and (optionally) the associated export fluxes. All exchange fluxes are oriented into the import reaction e.g. positive fluxes denote imports and negative fluxes exports. If `minimize_components` is a number larger 1 may return a DataFrame where each column is a minimal medium. Returns None if the minimization is infeasible (for instance if min_growth > maximum growth rate). Notes ----- Due to numerical issues the `minimize_components` option will usually only minimize the number of "large" import fluxes. Specifically, the detection limit is given by ``integrality_tolerance * max_bound`` where ``max_bound`` is the largest bound on an import reaction. Thus, if you are interested in small import fluxes as well you may have to adjust the integrality tolerance at first with `model.solver.configuration.tolerances.integrality = 1e-7` for instance. However, this will be *very* slow for large models especially with GLPK. """ exchange_rxns = find_boundary_types(model, "exchange") if isinstance(open_exchanges, bool): open_bound = 1000 else: open_bound = open_exchanges with model as mod: if open_exchanges: LOGGER.debug("Opening exchanges for %d imports.", len(exchange_rxns)) for rxn in exchange_rxns: rxn.bounds = (-open_bound, open_bound) LOGGER.debug("Applying objective value constraints.") obj_const = mod.problem.Constraint( mod.objective.expression, lb=min_objective_value, name="medium_obj_constraint") mod.add_cons_vars([obj_const]) mod.solver.update() mod.objective = Zero LOGGER.debug("Adding new media objective.") tol = mod.solver.configuration.tolerances.feasibility if minimize_components: add_mip_obj(mod) if isinstance(minimize_components, bool): minimize_components = 1 seen = set() best = num_components = mod.slim_optimize() if mod.solver.status != OPTIMAL: LOGGER.warning("Minimization of medium was infeasible.") return None exclusion = mod.problem.Constraint(Zero, ub=0) mod.add_cons_vars([exclusion]) mod.solver.update() media = [] for i in range(minimize_components): LOGGER.info("Finding alternative medium #%d.", (i + 1)) vars = [mod.variables["ind_" + s] for s in seen] if len(seen) > 0: exclusion.set_linear_coefficients( dict.fromkeys(vars, 1)) exclusion.ub = best - 1 num_components = mod.slim_optimize() if mod.solver.status != OPTIMAL or num_components > best: break medium = _as_medium(exchange_rxns, tol, exports=exports) media.append(medium) seen.update(medium[medium > 0].index) if len(media) > 1: medium = pd.concat(media, axis=1).fillna(0.0) medium.sort_index(axis=1, inplace=True) else: medium = media[0] else: add_linear_obj(mod) mod.slim_optimize() if mod.solver.status != OPTIMAL: LOGGER.warning("Minimization of medium was infeasible.") return None medium = _as_medium(exchange_rxns, tol, exports=exports) return medium
def minimal_medium(model, min_objective_value=0.1, exports=False, minimize_components=False, open_exchanges=False): """ Find the minimal growth medium for the model. Finds the minimal growth medium for the model which allows for model as well as individual growth. Here, a minimal medium can either be the medium requiring the smallest total import flux or the medium requiring the least components (ergo ingredients), which will be much slower due to being a mixed integer problem (MIP). Arguments --------- model : cobra.model The model to modify. min_objective_value : positive float or array-like object The minimum growth rate (objective) that has to be achieved. exports : boolean Whether to include export fluxes in the returned medium. Defaults to False which will only return import fluxes. minimize_components : boolean or positive int Whether to minimize the number of components instead of the total import flux. Might be more intuitive if set to True but may also be slow to calculate for large communities. If set to a number `n` will return up to `n` alternative solutions all with the same number of components. open_exchanges : boolean or number Whether to ignore currently set bounds and make all exchange reactions in the model possible. If set to a number all exchange reactions will be opened with (-number, number) as bounds. Returns ------- pandas.Series, pandas.DataFrame or None A series giving the import flux for each required import reaction and (optionally) the associated export fluxes. All exchange fluxes are oriented into the import reaction e.g. positive fluxes denote imports and negative fluxes exports. If `minimize_components` is a number larger 1 may return a DataFrame where each column is a minimal medium. Returns None if the minimization is infeasible (for instance if min_growth > maximum growth rate). Notes ----- Due to numerical issues the `minimize_components` option will usually only minimize the number of "large" import fluxes. Specifically, the detection limit is given by ``integrality_tolerance * max_bound`` where ``max_bound`` is the largest bound on an import reaction. Thus, if you are interested in small import fluxes as well you may have to adjust the integrality tolerance at first with `model.solver.configuration.tolerances.integrality = 1e-7` for instance. However, this will be *very* slow for large models especially with GLPK. """ exchange_rxns = find_boundary_types(model, "exchange") if isinstance(open_exchanges, bool): open_bound = 1000 else: open_bound = open_exchanges with model as mod: if open_exchanges: LOGGER.debug("Opening exchanges for %d imports.", len(exchange_rxns)) for rxn in exchange_rxns: rxn.bounds = (-open_bound, open_bound) LOGGER.debug("Applying objective value constraints.") obj_const = mod.problem.Constraint( mod.objective.expression, lb=min_objective_value, name="medium_obj_constraint") mod.add_cons_vars([obj_const]) mod.solver.update() mod.objective = Zero LOGGER.debug("Adding new media objective.") tol = mod.solver.configuration.tolerances.feasibility if minimize_components: add_mip_obj(mod) if isinstance(minimize_components, bool): minimize_components = 1 seen = set() best = num_components = mod.slim_optimize() if mod.solver.status != OPTIMAL: LOGGER.warning("Minimization of medium was infeasible.") return None exclusion = mod.problem.Constraint(Zero, ub=0) mod.add_cons_vars([exclusion]) mod.solver.update() media = [] for i in range(minimize_components): LOGGER.info("Finding alternative medium #%d.", (i + 1)) vars = [mod.variables["ind_" + s] for s in seen] if len(seen) > 0: exclusion.set_linear_coefficients( dict.fromkeys(vars, 1)) exclusion.ub = best - 1 num_components = mod.slim_optimize() if mod.solver.status != OPTIMAL or num_components > best: break medium = _as_medium(exchange_rxns, tol, exports=exports) media.append(medium) seen.update(medium[medium > 0].index) if len(media) > 1: medium = pd.concat(media, axis=1, sort=True).fillna(0.0) medium.sort_index(axis=1, inplace=True) else: medium = media[0] else: add_linear_obj(mod) mod.slim_optimize() if mod.solver.status != OPTIMAL: LOGGER.warning("Minimization of medium was infeasible.") return None medium = _as_medium(exchange_rxns, tol, exports=exports) return medium