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)