def crossover(community, sol, fluxes=False, pfba=False): """Get the crossover solution.""" gcs = sol.members.growth_rate.drop("medium") com_growth = sol.growth_rate logger.info("Starting crossover...") with community as com: logger.info("constraining growth rates.") context = get_context(community) if context is not None: context(partial(reset_min_community_growth, com)) reset_min_community_growth(com) com.variables.community_objective.lb = 0.0 com.variables.community_objective.ub = com_growth + 1e-6 com.objective = 1000.0 * com.variables.community_objective for sp in com.species: const = com.constraints["objective_" + sp] const.ub = gcs[sp] logger.info("finding closest feasible solution") s = com.optimize() if s is None: reset_solver(com) s = com.optimize() if s is not None: s = CommunitySolution(com, slim=not fluxes) for sp in com.species: com.constraints["objective_" + sp].ub = None if s is None: raise OptimizationError("crossover could not converge (status = %s)." % community.solver.status) s.objective_value /= 1000.0 return s
def elasticities(com, fraction=0.5, reactions=None, progress=True): """Calculate elasticities for reactions. Calculates elasticity coefficients using the specified reactions as response and exchange bounds (diet) and taxa abundances as effectors/parameters. Will use an arbitrary flux distribution as base. Arguments --------- com : micom.Community The community for wrhich to calculate elasticities. fraction : double The tradeoff to use for the cooperative tradeoff method. Fraction of maximal community growth to enforce. reactions : iterable A list of reactions to get elasticities for. Elements can either be reactions from the model, strings specifying the ids of reactions or ints specifying the indices of reactions. Defaults to using all reactions. progress : boolean Whether to shwo progress bars. Will show two, one for the diet optimizations and another one for the taxa abundances. Returns ------- pandas.DataFrame A data frame with the following columns: "reaction" - the exchange reaction (response), "taxon" - the taxon the reaction is from, "effector" - the parameter that was changed, "direction" - whether the flux runs in the forward or reverse direction, "elasticity" - the elasticity coefficient, "type" - the type of effector either "exchange" for diet or "abundance" for taxa abundances. """ growth_rate = None if reactions is None: reactions = com.reactions reactions = com.reactions.get_by_any(reactions) with com: context = get_context(com) context(partial(reset_min_community_growth, com)) by_medium = elasticities_by_medium( com, reactions, fraction, growth_rate, progress ) by_medium["type"] = "exchanges" by_abundance = elasticities_by_abundance( com, reactions, fraction, growth_rate, progress ) by_abundance["type"] = "abundance" both = pd.concat([by_medium, by_abundance]).reset_index(drop=True) both.loc[both.taxon == "m", "taxon"] = "medium" return both
def knock_out_metabolite(metabolite, force_steady_state=False): """'Knockout' a metabolite. This can be done in 2 ways: 1. Implementation follows the description in [1] "All fluxes around the metabolite M should be restricted to only produce the metabolite, for which balancing constraint of mass conservation is relaxed to allow nonzero values of the incoming fluxes whereas all outgoing fluxes are limited to zero." 2. Force steady state All reactions consuming the metabolite are restricted to only produce the metabolite. A demand reaction is added to sink the metabolite produced to keep the problem feasible under the S.v = 0 constraint. Knocking out a metabolite overrules the constraints set on the reactions producing the metabolite. Parameters ---------- force_steady_state: bool If True, uses approach 2. References ---------- .. [1] Kim, P.-J., Lee, D.-Y., Kim, T. Y., Lee, K. H., Jeong, H., Lee, S. Y., & Park, S. (2007). Metabolite essentiality elucidates robustness of Escherichia coli metabolism. PNAS, 104(34), 13638-13642 """ # restrict reactions to produce metabolite for rxn in metabolite.reactions: if rxn.metabolites[metabolite] > 0: rxn.bounds = (0, 0) if rxn.upper_bound < 0 \ else (0, rxn.upper_bound) elif rxn.metabolites[metabolite] < 0: rxn.bounds = (0, 0) if rxn.lower_bound > 0 \ else (rxn.lower_bound, 0) if force_steady_state: metabolite._model.add_boundary(metabolite, type="knock-out", lb=0, ub=1000, reaction_id="KO_{}".format( metabolite.id)) else: previous_bounds = metabolite.constraint.lb, metabolite.constraint.ub metabolite.constraint.lb, metabolite.constraint.ub = -1000, 1000 context = get_context(metabolite) if context: def reset(): metabolite.constraint.lb, metabolite.constraint.ub = previous_bounds context(reset)
def test_get_context(model: "Model") -> None: """Test if context retrieval is working.""" with model as model: # reverse optimization direcion model.objective_direction = "min" # retrieve context context = get_context(model) # check size of the context if context: assert context.size() == 1 # there shouldn't be any history if context: assert context.size() == 0
def exchange_elasticities(com, fraction=1.0, progress=True): """Calculate elasticities for exchange reactions. Calculates elasticity coefficients using the exchange reactions as response and exchange bounds (diet) and taxa abundances as effectors/parameters. Will use an arbitrary flux distribution as base. Arguments --------- com : micom.Community The community for wrhich to calculate elasticities. fraction : double The tradeoff to use for the cooperative tradeoff method. Fraction of maximal community growth to enforce. progress : boolean Whether to shwo progress bars. Will show two, one for the diet optimizations and another one for the taxa abundances. Returns ------- pandas.DataFrame A data frame with the following columns: "reaction" - the exchange reaction (response), "effector" - the parameter that was changed, "direction" - whether the flux runs in the forward od reverse direction, "elasticity" - the elasticity coefficient, "type" - the type of effector either "exchange" for diet or "abundance" for taxa abundances. """ growth_rate = None with com: context = get_context(com) context(partial(reset_min_community_growth, com)) rxns = com.exchanges by_medium = elasticities_by_medium(com, rxns, fraction, growth_rate, progress) by_medium["type"] = "exchanges" by_abundance = elasticities_by_abundance(com, rxns, fraction, growth_rate, progress) by_abundance["type"] = "abundance" return pd.concat([by_medium, by_abundance])
def minimal_medium( community, community_growth, exchanges=None, min_growth=0.0, exports=False, minimize_components=False, open_exchanges=False, solution=False, ): """Find the minimal growth medium for the community. Finds the minimal growth medium for the community which allows for community 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). Arguments --------- community : micom.Community The community to modify. community_growth : positive float The minimum community-wide growth rate. exchanges : list of cobra.Reactions The list of exchange reactions that are penalized. min_growth : positive float or array-like object. The minimum growth rate for each individual in the community. Either a single value applied to all individuals or one value for each. exports : boolean Whether to include export fluxes in the returned medium. Defaults to False which will only return import fluxes. minimize_components : boolean 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. 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. solution : boolean Whether to also return the entire solution and all fluxes for the minimal medium. Returns ------- pandas.Series or dict A series {rid: flux} giving the import flux for each required import reaction. If `solution` is True retuns a dictionary {"medium": panas.Series, "solution": micom.CommunitySolution}. """ logger.info("calculating minimal medium for %s" % community.id) boundary_rxns = community.exchanges if isinstance(open_exchanges, bool): open_bound = 1000 else: open_bound = open_exchanges min_growth = _format_min_growth(min_growth, community.species) with community as com: if open_exchanges: logger.info("opening exchanges for %d imports" % len(boundary_rxns)) for rxn in boundary_rxns: rxn.bounds = (-open_bound, open_bound) logger.info("applying growth rate constraints") context = get_context(community) if context is not None: context(partial(reset_min_community_growth, com)) com.variables.community_objective.lb = community_growth _apply_min_growth(community, min_growth) com.objective = Zero logger.info("adding new media objective") if minimize_components: add_mip_obj(com, boundary_rxns) else: add_linear_obj(com, boundary_rxns) sol = com.optimize(fluxes=True, pfba=False) if sol is None: logger.warning("minimization of medium was unsuccessful") return None logger.info("formatting medium") medium = pd.Series() tol = community.solver.configuration.tolerances.feasibility for rxn in boundary_rxns: export = len(rxn.reactants) == 1 flux = sol.fluxes.loc["medium", rxn.id] if abs(flux) < tol: continue if export: medium[rxn.id] = -flux elif not export: medium[rxn.id] = flux if not exports: medium = medium[medium > 0] if solution: return {"medium": medium, "solution": sol} else: return medium