def add_linear_obj(community, exchanges, weights): """Add a linear version of a minimal medium to the community. Changes the optimization objective to finding the growth medium requiring the smallest total import flux:: minimize sum w_i|r_i| for r_i in import_reactions Arguments --------- community : micom.Community The community to modify. exchanges : list of cobra.Reaction The reactions to constrain. weights : dict Maps each exchange reaction to a weight that is used in the minimization. """ check_modification(community) coefs = {} for rxn in exchanges: export = len( rxn.reactants) == 1 or (len(rxn.metabolites) == 2 and rxn.products[0].compartment == "m") met = list(rxn.metabolites)[0] if export: coefs[rxn.reverse_variable] = weights[met] else: coefs[rxn.forward_variable] = weights[met] community.objective.set_linear_coefficients(coefs) community.objective.direction = "min" community.modification = "minimal medium linear"
def add_linear_obj(community, exchanges): """Add a linear version of a minimal medium to the community. 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 --------- community : micom.Community The community to modify. exchanges : list of cobra.Reaction The reactions to constrain. """ check_modification(community) coefs = {} for rxn in exchanges: export = len(rxn.reactants) == 1 if export: coefs[rxn.reverse_variable] = 1.0 else: coefs[rxn.forward_variable] = 1.0 community.objective.set_linear_coefficients(coefs) community.objective.direction = "min" community.modification = "minimal medium linear"
def cooperative_tradeoff(community, min_growth, fraction, fluxes, pfba): """Find the best tradeoff between community and individual growth.""" with community as com: check_modification(community) min_growth = _format_min_growth(min_growth, community.species) _apply_min_growth(community, min_growth) com.objective = 1000.0 * com.variables.community_objective min_growth = optimize_with_retry( com, message="could not get community growth rate.") / 1000.0 if not isinstance(fraction, Sized): fraction = [fraction] else: fraction = np.sort(fraction)[::-1] # Add needed variables etc. regularize_l2_norm(com, 0.0) results = [] for fr in fraction: com.variables.community_objective.lb = fr * min_growth com.variables.community_objective.ub = min_growth sol = solve(community, fluxes=fluxes, pfba=pfba) if sol.status != OPTIMAL: sol = crossover(com, sol, fluxes=fluxes, pfba=pfba) results.append((fr, sol)) if len(results) == 1: return results[0][1] return pd.DataFrame.from_records(results, columns=["tradeoff", "solution"])
def cooperative_tradeoff(community, min_growth, fraction, fluxes, pfba): """Find the best tradeoff between community and individual growth.""" with community as com: solver = interface_to_str(community.problem) check_modification(community) min_growth = _format_min_growth(min_growth, community.taxa) _apply_min_growth(community, min_growth) com.objective = com.scale * com.variables.community_objective min_growth = (optimize_with_retry( com, message="could not get community growth rate.") / com.scale) if not isinstance(fraction, Sized): fraction = [fraction] else: fraction = np.sort(fraction)[::-1] # Add needed variables etc. regularize_l2_norm(com, 0.0) results = [] for fr in fraction: com.variables.community_objective.lb = fr * min_growth com.variables.community_objective.ub = min_growth sol = solve(community, fluxes=fluxes, pfba=pfba) # OSQP is better with QPs then LPs # so it won't get better with the crossover if (sol.status != OPTIMAL and solver != "osqp"): sol = crossover(com, sol, fluxes=fluxes, pfba=pfba) results.append((fr, sol)) if len(results) == 1: return results[0][1] return pd.DataFrame.from_records(results, columns=["tradeoff", "solution"])
def add_mip_obj(community, exchanges): """Add a mixed-integer version of a minimal medium to the community. Changes the optimization objective to finding the medium with the least components:: minimize size(R) where R part of import_reactions Arguments --------- community : micom.Community The community to modify. exchanges : list of cobra.Reaction The reactions to constrain. """ check_modification(community) if len(community.variables) > 1e4: logger.warning( "the MIP version of minimal media is extremely slow for" " models that large :(" ) boundary_rxns = exchanges M = max(np.max(np.abs(r.bounds)) for r in boundary_rxns) prob = community.problem coefs = {} to_add = [] for rxn in boundary_rxns: export = len(rxn.reactants) == 1 or ( len(rxn.reactants) == 2 and rxn.products[0].compartment == "m" ) indicator = prob.Variable("ind_" + rxn.id, lb=0, ub=1, type="binary") if export: vrv = rxn.reverse_variable indicator_const = prob.Constraint( vrv - indicator * M, ub=0, name="ind_constraint_" + rxn.id ) else: vfw = rxn.forward_variable indicator_const = prob.Constraint( vfw - indicator * M, ub=0, name="ind_constraint_" + rxn.id ) to_add.extend([indicator, indicator_const]) coefs[indicator] = 1 community.add_cons_vars(to_add) community.solver.update() community.objective.set_linear_coefficients(coefs) community.objective.direction = "min" community.modification = "minimal medium mixed-integer"
def knockout_taxa( community, taxa, fraction, method, progress, diag=True ): """Knockout a taxon from the community.""" with community as com: check_modification(com) min_growth = _format_min_growth(0.0, com.taxa) _apply_min_growth(com, min_growth) com.objective = com.scale * com.variables.community_objective community_min_growth = ( optimize_with_retry(com, "could not get community growth rate.") / com.scale ) regularize_l2_norm(com, fraction * community_min_growth) old = com.optimize().members["growth_rate"] results = [] iter = track(taxa, description="Knockouts") if progress else taxa for sp in iter: with com: logger.info("getting growth rates for " "%s knockout." % sp) [ r.knock_out() for r in com.reactions.query( lambda ri: ri.community_id == sp ) ] sol = optimize_with_fraction(com, fraction) new = sol.members["growth_rate"] if "change" in method: new = new - old if "relative" in method: new /= old results.append(new) ko = pd.DataFrame(results, index=taxa).drop("medium", 1) ko = ko.loc[ko.index.sort_values(), ko.columns.sort_values()] if not diag: np.fill_diagonal(ko.values, np.NaN) return ko
def knockout_species(community, species, fraction, method, progress, diag=True): """Knockout a species from the community.""" with community as com: check_modification(com) min_growth = _format_min_growth(0.0, com.species) _apply_min_growth(com, min_growth) com.objective = 1000.0 * com.variables.community_objective community_min_growth = ( optimize_with_retry(com, "could not get community growth rate.") / 1000.0) regularize_l2_norm(com, fraction * community_min_growth) old = com.optimize().members["growth_rate"] results = [] if progress: species = tqdm(species, unit="knockout(s)") for sp in species: with com: logger.info("getting growth rates for " "%s knockout." % sp) [ r.knock_out() for r in com.reactions.query( lambda ri: ri.community_id == sp) ] sol = optimize_with_fraction(com, fraction) new = sol.members["growth_rate"] if "change" in method: new = new - old if "relative" in method: new /= old results.append(new) ko = pd.DataFrame(results, index=species).drop("medium", 1) if not diag: np.fill_diagonal(ko.values, np.NaN) return ko
def add_moma_optcom(community, min_growth, linear=False): """Add a dualized MOMA version of OptCom. Solves a MOMA (minimization of metabolic adjustment) formulation of OptCom given by:: minimize cooperativity_cost s.t. maximize community_objective s.t. Sv = 0 lb >= v >= ub where community_cost = sum (growth_rate - max_growth)**2 if linear=False or community_cost = sum |growth_rate - max_growth| if linear=True Arguments --------- community : micom.Community The community to modify. 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. linear : boolean Whether to use a non-linear (sum of squares) or linear version of the cooperativity cost. If set to False requires a QP-capable solver. """ logger.info("adding dual %s moma to %s" % ("linear" if linear else "quadratic", community.id)) check_modification(community) min_growth = _format_min_growth(min_growth, community.taxa) prob = community.solver.interface old_obj = community.objective coefs = old_obj.get_linear_coefficients(old_obj.variables) # Get maximum individual growth rates max_gcs = community.optimize_all(progress=False) _apply_min_growth(community, min_growth) dual_coefs = fast_dual(community) coefs.update({v: -coef for v, coef in dual_coefs.items()}) obj_constraint = prob.Constraint(Zero, lb=0, ub=0, name="optcom_suboptimality") community.add_cons_vars([obj_constraint]) community.solver.update() obj_constraint.set_linear_coefficients(coefs) obj_expr = Zero logger.info("adding expressions for %d taxa" % len(community.taxa)) for sp in community.taxa: v = prob.Variable("gc_constant_" + sp, lb=max_gcs[sp], ub=max_gcs[sp]) community.add_cons_vars([v]) taxa_obj = community.constraints["objective_" + sp] ex = v - taxa_obj.expression if not linear: ex = ex**2 obj_expr += ex.expand() community.objective = prob.Objective(obj_expr, direction="min") community.modification = "moma optcom" logger.info("finished dual moma to %s" % community.id)
def add_dualized_optcom(community, min_growth): """Add dual Optcom variables and constraints to a community. Uses the original formulation of OptCom and solves the following multi-objective problem:: maximize community_growth s.t. maximize growth_rate_i for all i s.t. Sv_i = 0 lb_i >= v_i >= ub_i Notes ----- This method will only find one arbitrary solution from the Pareto front. There may exist several other optimal solutions. Arguments --------- community : micom.Community The community to modify. 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. """ logger.info("adding dual optcom to %s" % community.id) check_modification(community) min_growth = _format_min_growth(min_growth, community.taxa) prob = community.solver.interface # Temporarily subtitute objective with sum of individual objectives # for correct dual variables old_obj = community.objective community.objective = Zero for sp in community.taxa: taxa_obj = community.constraints["objective_" + sp] community.objective += taxa_obj.expression _apply_min_growth(community, min_growth) dual_coefs = fast_dual(community) logger.info("adding expressions for %d taxa" % len(community.taxa)) for sp in community.taxa: primal_const = community.constraints["objective_" + sp] coefs = primal_const.get_linear_coefficients(primal_const.variables) coefs.update({ dual_var: -coef for dual_var, coef in dual_coefs.items() if sp in dual_var.name }) obj_constraint = prob.Constraint(Zero, lb=0, ub=0, name="optcom_suboptimality_" + sp) community.add_cons_vars([obj_constraint]) community.solver.update() obj_constraint.set_linear_coefficients(coefs) community.objective = old_obj community.modification = "dual optcom" logger.info("finished adding dual optcom to %s" % community.id)