Beispiel #1
0
 def test_inequality(self, solver_test):
     solver, old_solution, infeasible_model = solver_test
     # The space enclosed by the constraints is a 2D triangle with
     # vertexes as (3, 0), (1, 2), and (0, 1)
     # c1 encodes y - x > 1 ==> y > x - 1
     # c2 encodes y + x < 3 ==> y < 3 - x
     c1 = Metabolite("c1")
     c2 = Metabolite("c2")
     x = Reaction("x")
     x.lower_bound = 0
     y = Reaction("y")
     y.lower_bound = 0
     x.add_metabolites({c1: -1, c2: 1})
     y.add_metabolites({c1: 1, c2: 1})
     c1._bound = 1
     c1._constraint_sense = "G"
     c2._bound = 3
     c2._constraint_sense = "L"
     m = Model()
     m.add_reactions([x, y])
     # test that optimal values are at the vertices
     m.objective = "x"
     assert abs(solver.solve(m).f - 1.0) < 10 ** -3
     assert abs(solver.solve(m).x_dict["y"] - 2.0) < 10 ** -3
     m.objective = "y"
     assert abs(solver.solve(m).f - 3.0) < 10 ** -3
     assert abs(
         solver.solve(m, objective_sense="minimize").f - 1.0) < 10 ** -3
Beispiel #2
0
 def test_quadratic(self, solver_test):
     solver, old_solution, infeasible_model = solver_test
     if not hasattr(solver, "set_quadratic_objective"):
         pytest.skip("no qp support")
     c = Metabolite("c")
     c._bound = 2
     x = Reaction("x")
     x.objective_coefficient = -0.5
     x.lower_bound = 0.
     y = Reaction("y")
     y.objective_coefficient = -0.5
     y.lower_bound = 0.
     x.add_metabolites({c: 1})
     y.add_metabolites({c: 1})
     m = Model()
     m.add_reactions([x, y])
     lp = solver.create_problem(m)
     quadratic_obj = scipy.sparse.eye(2) * 2
     solver.set_quadratic_objective(lp, quadratic_obj)
     solver.solve_problem(lp, objective_sense="minimize")
     solution = solver.format_solution(lp, m)
     assert solution.status == "optimal"
     # Respecting linear objectives also makes the objective value 1.
     assert abs(solution.f - 1.) < 10 ** -3
     assert abs(solution.x_dict["y"] - 1.) < 10 ** -3
     assert abs(solution.x_dict["y"] - 1.) < 10 ** -3
     # When the linear objectives are removed the objective value is 2.
     solver.change_variable_objective(lp, 0, 0.)
     solver.change_variable_objective(lp, 1, 0.)
     solver.solve_problem(lp, objective_sense="minimize")
     solution = solver.format_solution(lp, m)
     assert solution.status == "optimal"
     assert abs(solution.f - 2.) < 10 ** -3
     # test quadratic from solve function
     solution = solver.solve(m, quadratic_component=quadratic_obj,
                             objective_sense="minimize")
     assert solution.status == "optimal"
     assert abs(solution.f - 1.) < 10 ** -3
     c._bound = 6
     z = Reaction("z")
     x.objective_coefficient = 0.
     y.objective_coefficient = 0.
     z.lower_bound = 0.
     z.add_metabolites({c: 1})
     m.add_reaction(z)
     solution = solver.solve(m, quadratic_component=scipy.sparse.eye(3),
                             objective_sense="minimize")
     # should be 12 not 24 because 1/2 (V^T Q V)
     assert solution.status == "optimal"
     assert abs(solution.f - 6) < 10 ** -3
     assert abs(solution.x_dict["x"] - 2) < 10 ** -6
     assert abs(solution.x_dict["y"] - 2) < 10 ** -6
     assert abs(solution.x_dict["z"] - 2) < 10 ** -6
Beispiel #3
0
 def test_change_coefficient(self, solver_test):
     solver, old_solution, infeasible_model = solver_test
     c = Metabolite("c")
     c._bound = 6
     x = Reaction("x")
     x.lower_bound = 1.
     y = Reaction("y")
     y.lower_bound = 0.
     x.add_metabolites({c: 1})
     z = Reaction("z")
     z.add_metabolites({c: 1})
     z.objective_coefficient = 1
     m = Model("test_model")
     m.add_reactions([x, y, z])
     # change an existing coefficient
     lp = solver.create_problem(m)
     solver.solve_problem(lp)
     sol1 = solver.format_solution(lp, m)
     assert sol1.status == "optimal"
     solver.change_coefficient(lp, 0, 0, 2)
     solver.solve_problem(lp)
     sol2 = solver.format_solution(lp, m)
     assert sol2.status == "optimal"
     assert abs(sol1.f - 5.0) < 10 ** -3
     assert abs(sol2.f - 4.0) < 10 ** -3
     # change a new coefficient
     z.objective_coefficient = 0.
     y.objective_coefficient = 1.
     lp = solver.create_problem(m)
     solver.change_coefficient(lp, 0, 1, 2)
     solver.solve_problem(lp)
     solution = solver.format_solution(lp, m)
     assert solution.status == "optimal"
     assert abs(solution.x_dict["y"] - 2.5) < 10 ** -3
Beispiel #4
0
 def test_solve_mip(self, solver_test):
     solver, old_solution, infeasible_model = solver_test
     if not hasattr(solver, "_SUPPORTS_MILP") or not solver._SUPPORTS_MILP:
         pytest.skip("no milp support")
     cobra_model = Model('MILP_implementation_test')
     constraint = Metabolite("constraint")
     constraint._bound = 2.5
     x = Reaction("x")
     x.lower_bound = 0.
     x.objective_coefficient = 1.
     x.add_metabolites({constraint: 2.5})
     y = Reaction("y")
     y.lower_bound = 0.
     y.objective_coefficient = 1.
     y.add_metabolites({constraint: 1.})
     cobra_model.add_reactions([x, y])
     float_sol = solver.solve(cobra_model)
     # add an integer constraint
     y.variable_kind = "integer"
     int_sol = solver.solve(cobra_model)
     assert abs(float_sol.f - 2.5) < 10 ** -5
     assert abs(float_sol.x_dict["y"] - 2.5) < 10 ** -5
     assert int_sol.status == "optimal"
     assert abs(int_sol.f - 2.2) < 10 ** -3
     assert abs(int_sol.x_dict["y"] - 2.0) < 10 ** -3
 def test_canonical_form(self, model):
     # add G constraint to test
     g_constr = Metabolite("SUCCt2_2__test_G_constraint")
     g_constr._constraint_sense = "G"
     g_constr._bound = 5.0
     model.reactions.get_by_id("SUCCt2_2").add_metabolites({g_constr: 1})
     assert abs(model.optimize("maximize").f - 0.855) < 0.001
     # convert to canonical form
     model = canonical_form(model)
     assert abs(model.optimize("maximize").f - 0.855) < 10**-3
Beispiel #6
0
 def test_canonical_form(self, model):
     # add G constraint to test
     g_constr = Metabolite("SUCCt2_2__test_G_constraint")
     g_constr._constraint_sense = "G"
     g_constr._bound = 5.0
     model.reactions.get_by_id("SUCCt2_2").add_metabolites({g_constr: 1})
     assert abs(model.optimize("maximize").f - 0.855) < 0.001
     # convert to canonical form
     model = canonical_form(model)
     assert abs(model.optimize("maximize").f - 0.855) < 10 ** -3
Beispiel #7
0
def construct_loopless_model(cobra_model):
    """Construct a loopless model.

    This adds MILP constraints to prevent flux from proceeding in a loop, as
    done in http://dx.doi.org/10.1016/j.bpj.2010.12.3707
    Please see the documentation for an explanation of the algorithm.

    This must be solved with an MILP capable solver.

    """
    # copy the model and make it irreversible
    model = cobra_model.copy()
    convert_to_irreversible(model)
    max_ub = max(model.reactions.list_attr("upper_bound"))
    # a dict for storing S^T
    thermo_stoic = {
        "thermo_var_" + metabolite.id: {}
        for metabolite in model.metabolites
    }
    # Slice operator is so that we don't get newly added metabolites
    original_metabolites = model.metabolites[:]
    for reaction in model.reactions[:]:
        # Boundary reactions are not subjected to these constraints
        if len(reaction._metabolites) == 1:
            continue
        # populate the S^T dict
        bound_id = "thermo_bound_" + reaction.id
        for met, stoic in iteritems(reaction._metabolites):
            thermo_stoic["thermo_var_" + met.id][bound_id] = stoic
        # I * 1000 > v --> I * 1000 - v > 0
        reaction_ind = Reaction(reaction.id + "_indicator")
        reaction_ind.variable_kind = "integer"
        reaction_ind.upper_bound = 1
        reaction_ub = Metabolite(reaction.id + "_ind_ub")
        reaction_ub._constraint_sense = "G"
        reaction.add_metabolites({reaction_ub: -1})
        reaction_ind.add_metabolites({reaction_ub: max_ub})
        # This adds a compensating term for 0 flux reactions, so we get
        # S^T x - (1 - I) * 1001 < -1 which becomes
        # S^T x < 1000 for 0 flux reactions and
        # S^T x < -1 for reactions with nonzero flux.
        reaction_bound = Metabolite(bound_id)
        reaction_bound._constraint_sense = "L"
        reaction_bound._bound = max_ub
        reaction_ind.add_metabolites({reaction_bound: max_ub + 1})
        model.add_reaction(reaction_ind)
    for metabolite in original_metabolites:
        metabolite_var = Reaction("thermo_var_" + metabolite.id)
        metabolite_var.lower_bound = -max_ub
        model.add_reaction(metabolite_var)
        metabolite_var.add_metabolites({
            model.metabolites.get_by_id(k): v
            for k, v in iteritems(thermo_stoic[metabolite_var.id])
        })
    return model
Beispiel #8
0
 def test_canonical_form(self):
     model = create_test_model("textbook")
     # add G constraint to test
     g_constr = Metabolite("SUCCt2_2__test_G_constraint")
     g_constr._constraint_sense = "G"
     g_constr._bound = 5.0
     model.reactions.get_by_id("SUCCt2_2").add_metabolites({g_constr: 1})
     self.assertAlmostEqual(model.optimize("maximize").f, 0.855, places=3)
     # convert to canonical form
     model = canonical_form(model)
     self.assertAlmostEqual(model.optimize("maximize").f, 0.855, places=3)
Beispiel #9
0
 def test_canonical_form(self):
     model = create_test_model("textbook")
     # add G constraint to test
     g_constr = Metabolite("SUCCt2_2__test_G_constraint")
     g_constr._constraint_sense = "G"
     g_constr._bound = 5.0
     model.reactions.get_by_id("SUCCt2_2").add_metabolites({g_constr: 1})
     self.assertAlmostEqual(model.optimize("maximize").f, 0.855, places=3)
     # convert to canonical form
     model = canonical_form(model)
     self.assertAlmostEqual(model.optimize("maximize").f, 0.855, places=3)
Beispiel #10
0
def construct_loopless_model(cobra_model):
    """Construct a loopless model.

    This adds MILP constraints to prevent flux from proceeding in a loop, as
    done in http://dx.doi.org/10.1016/j.bpj.2010.12.3707
    Please see the documentation for an explanation of the algorithm.

    This must be solved with an MILP capable solver.

    """
    # copy the model and make it irreversible
    model = cobra_model.copy()
    convert_to_irreversible(model)
    max_ub = max(model.reactions.list_attr("upper_bound"))
    # a dict for storing S^T
    thermo_stoic = {"thermo_var_" + metabolite.id: {}
                    for metabolite in model.metabolites}
    # Slice operator is so that we don't get newly added metabolites
    original_metabolites = model.metabolites[:]
    for reaction in model.reactions[:]:
        # Boundary reactions are not subjected to these constraints
        if len(reaction._metabolites) == 1:
            continue
        # populate the S^T dict
        bound_id = "thermo_bound_" + reaction.id
        for met, stoic in iteritems(reaction._metabolites):
            thermo_stoic["thermo_var_" + met.id][bound_id] = stoic
        # I * 1000 > v --> I * 1000 - v > 0
        reaction_ind = Reaction(reaction.id + "_indicator")
        reaction_ind.variable_kind = "integer"
        reaction_ind.upper_bound = 1
        reaction_ub = Metabolite(reaction.id + "_ind_ub")
        reaction_ub._constraint_sense = "G"
        reaction.add_metabolites({reaction_ub: -1})
        reaction_ind.add_metabolites({reaction_ub: max_ub})
        # This adds a compensating term for 0 flux reactions, so we get
        # S^T x - (1 - I) * 1001 < -1 which becomes
        # S^T x < 1000 for 0 flux reactions and
        # S^T x < -1 for reactions with nonzero flux.
        reaction_bound = Metabolite(bound_id)
        reaction_bound._constraint_sense = "L"
        reaction_bound._bound = max_ub
        reaction_ind.add_metabolites({reaction_bound: max_ub + 1})
        model.add_reaction(reaction_ind)
    for metabolite in original_metabolites:
        metabolite_var = Reaction("thermo_var_" + metabolite.id)
        metabolite_var.lower_bound = -max_ub
        model.add_reaction(metabolite_var)
        metabolite_var.add_metabolites(
            {model.metabolites.get_by_id(k): v
             for k, v in iteritems(thermo_stoic[metabolite_var.id])})
    return model
def convert_to_irreversible_with_indicators(cobra_model,reaction_id_list,metabolite_list, mutually_exclusive_directionality_constraint = False,label_model=None):
    #Function modified from the work by : """Schmidt BJ1, Ebrahim A, Metz TO, Adkins JN, Palsson B, Hyduke DR. GIM3E: condition-specific models of cellular metabolism developed from metabolomics and expression data Bioinformatics. 2013 Nov 15;29(22):2900-8. doi: 10.1093/bioinformatics/btt493. Epub 2013 Aug 23."""
    """Will break all of the reversible reactions into two separate irreversible
     reactions with different directions.  This function call modified from
     a version in the core cobra to facilitate the MILP formulation and
     include gene_reaction_rules with the reverse reaction
   
     Arguments:
      cobra_model: A model object which will be modified in place.
      mutually_exclusive_directionality_constraint: Boolean.  If True, turnover 
       reactions are constructed to serve as MILP constraints to prevent loops.
      
     Returns:
      None, cobra_model is modified in place
    
    
    
    
     """
    reactions_to_add = []
    from cobra.core.Reaction import Reaction
    from cobra.core import Metabolite
    reactions_to_make_irreversible=[]
    for x in reaction_id_list:
        reactions_to_make_irreversible.append(cobra_model.reactions.get_by_id(x))
    """for x in lexs:
        reactions_to_make_irreversible.append(cobra_model.reactions.get_by_id(x))"""
    
    #If a label model object  is provided make sure all experimentally measured metabolites (or at least one of the metabolites in the pool) is produced
    full_metabolite_list=copy.copy(metabolite_list)
    print full_metabolite_list
    if label_model!=None:
       emus=[]
       for condition in label_model.experimental_dict:
           for emu in label_model.experimental_dict[condition]:
               if emu not in emus:
                  emus.append(emu)
       measured_metabolite_dict={}
       
       for emu in emus:
           iso_id=str(label_model.emu_dict[emu]["met_id"])
           #print label_model.id_isotopomer_object_dict
           #isotopomer_object=label_model.id_isotopomer_object_dict[iso_id]
           metabolites=label_model.isotopomer_id_metabolite_id_dict[iso_id]
           print [iso_id,label_model.isotopomer_id_metabolite_id_dict[iso_id]]
           if isinstance(metabolites,list):
              for metabolite in metabolites:
                  full_metabolite_list.append(metabolite)
           else:
              full_metabolite_list.append(metabolites)
    
    for metabolites in full_metabolite_list:
       print metabolites
       if not isinstance(metabolites,list):
          metabolites=[metabolites]
       for metabolite in metabolites:
          print metabolite
          the_metabolite=cobra_model.metabolites.get_by_id(metabolite)
          for x in the_metabolite.reactions:
             if x not in reactions_to_make_irreversible:
              reactions_to_make_irreversible.append(x)    
                  
    for reaction in reactions_to_make_irreversible:
        # Potential artifact because a reaction might run backwards naturally
        # and this would result in adding an empty reaction to the
        # model in addition to the reverse reaction.
        if reaction.lower_bound < 0:
            #reverse_reaction = Reaction(reaction.id + "_reverse")
            reverse_reaction = reaction.copy()
            reverse_reaction.id = reaction.id + "_reverse"
            reverse_reaction.lower_bound = max(0,-1*reaction.upper_bound)
            reverse_reaction.upper_bound = reaction.lower_bound * -1.
            reaction.lower_bound = 0
            if reaction.upper_bound<0:
               reaction.upper_bound=0
            # Make the directions aware of each other
            reaction.notes["reflection"] = reverse_reaction.id
            reverse_reaction.notes["reflection"] = reaction.id
            reaction_dict = {}
            current_metabolites = [x for x in reaction.metabolites]
            for the_metabolite in current_metabolites:
                reaction_dict[the_metabolite] = -2 * reaction.get_coefficient(the_metabolite.id)
            reverse_reaction.add_metabolites(reaction_dict)
            reactions_to_add.append(reverse_reaction)
            # Also: GPRs should already copy
            # reverse_reaction.gene_reaction_rule = reaction.gene_reaction_rule
            # reverse_reaction._genes = reaction._genes
            
            if mutually_exclusive_directionality_constraint:
                # A continuous reaction bounded by 0., 1.
                # Serves as a source for the indicator metabolites
                tmp_source = Reaction('IRRMILP_direction_constraint_source_for_%s_and_%s'
                                                           %(reaction.id,
                                                             reverse_reaction.id))
                tmp_source.upper_bound = 1.
                tmp_source.lower_bound = 0.
                # The reverse indicator reaction is
                # an integer-valued reaction bounded by 0,1
                # that activates flux to the reverse reaction
                # and deactivates the forward reaction only when it is
                # turned on to 1
                tmp_indicator = Reaction('IRRMILP_reverse_indicator_for_%s_and_%s'
                                                           %(reaction.id,
                                                             reverse_reaction.id))
                tmp_indicator.upper_bound = 1
                tmp_indicator.lower_bound = 0
                tmp_indicator.variable_kind = 'integer'                    
                flux_constraint_forward = Metabolite(id = 
                     'IRRMILP_direction_constraint_for_%s'%reaction.id)
                flux_constraint_reverse = Metabolite(id = 
                     'IRRMILP_direction_constraint_for_%s'%reverse_reaction.id)
                flux_constraint_reverse._constraint_sense = 'G'
                flux_constraint_reverse._bound = 0.
                
                tmp_source.add_metabolites({flux_constraint_forward: 1})
                
                tmp_indicator.add_metabolites({flux_constraint_forward: -1,
                                      flux_constraint_reverse: 1})
                if reaction.upper_bound != 0:
                        reaction.add_metabolites({flux_constraint_forward: -1./reaction.upper_bound})
                else:
                    # could put 1.01 X the tolerance here,
                    # This is arbitrary.  Use 0.001
                    # since 1000 is a typical upper bound
                    reaction.add_metabolites({flux_constraint_forward: -0.001})
                if reverse_reaction.upper_bound != 0:
                    reverse_reaction.add_metabolites({flux_constraint_reverse: -1./reverse_reaction.upper_bound})
                else:
                    reverse_reaction.add_metabolites({flux_constraint_reverse: -0.001})
                reactions_to_add.append(tmp_indicator)
                reactions_to_add.append(tmp_source)
    cobra_model.add_reactions(reactions_to_add)
Beispiel #12
0
def optimize_minimum_flux(model, objective_sense='maximize',
                 tolerance_optimality=1e-8, tolerance_feasibility=1e-8):
    # return the flux distribution in which the total amount of fluxes is minimum while the growth is maximum
    #Get the optimal wt objective value and adjust based on optimality tolerances
    model.optimize()
    optimal_value = deepcopy(model.solution.f)
    # print('opt val: %s' % optimal_value)
    if objective_sense == 'maximize':
        optimal_value = floor(optimal_value/tolerance_optimality)*tolerance_optimality
    else:
        optimal_value = ceil(optimal_value/tolerance_optimality)*tolerance_optimality
    # print('adjusted opt val: %s' % optimal_value)
    #Add in the virtual objective metabolite to constrain the wt_model to the space where
    #the objective was maximal
    objective_metabolite = Metabolite('objective metabolite')
    objective_metabolite._bound = optimal_value
    if objective_sense == 'maximize':
        objective_metabolite._constraint_sense = 'G'
    else:
        objective_metabolite._constraint_sense = 'L'
    # print('objm const sense: %s, objm bound: %s' % (objective_metabolite._constraint_sense, objective_metabolite._bound))

    # construct irreversible model to assure all flux values are positive
    irreve_model = model.copy()
    # this is necessary to avoid invalid bound error when model is changed to irreversible
    for r in irreve_model.reactions:
        if r.upper_bound < 0:
            reverse_reaction = Reaction(r.id + "_reverse")
            reverse_reaction.lower_bound = r.upper_bound * -1
            reverse_reaction.upper_bound = r.lower_bound * -1
            reverse_reaction.objective_coefficient = r.objective_coefficient * -1
            reaction_dict = dict([(k, v*-1)
                                  for k, v in r.metabolites.items()])
            reverse_reaction.add_metabolites(reaction_dict)
            irreve_model.add_reaction(reverse_reaction)
            r.upper_bound, r.lower_bound = 0, 0

    cobra.manipulation.modify.convert_to_irreversible(irreve_model)
    objective_reaction_coefficient_dict = dict([(x.id, x.objective_coefficient)
                                                    for x in model.reactions
                                                    if x.objective_coefficient])
    # this couples the objective reaction to the virtual metabolite
    [irreve_model.reactions.get_by_id(k).add_metabolites({objective_metabolite: v})
     for k, v in objective_reaction_coefficient_dict.items()]
    # print('irregular metabolites: %s' % [(m.id, m._constraint_sense, m._bound)
    #                                      for m in irreve_model.metabolites if m._constraint_sense != 'E' or m._bound != 0])

    # minimize the sum of fluxes
    for r in irreve_model.reactions:
        r.objective_coefficient = 1
    # print([r.id for r in irreve_model.reactions if r.objective_coefficient != 1])
    # print(tolerance_feasibility)
    irreve_model.optimize(objective_sense='minimize',
                          tolerance_feasibility=tolerance_feasibility)

    # adjust this to the solution of wt_model
    original_flux = model.solution.x_dict
    irreve_flux = irreve_model.solution.x_dict
    for k in original_flux.keys():
        original_flux[k] = irreve_flux[k]
        # if reverse reaction exists and its flux is not zero, assign as a negative flux in wt_flux
        if k + '_reverse' in irreve_flux.keys() and irreve_flux[k + '_reverse'] != 0:
            if irreve_flux[k] != 0:
                print('Attention: non-optimal solution')
            original_flux[k] = -irreve_flux[k + '_reverse']

    model.solution.status = irreve_model.solution.status
    model.solution.f = sum([irreve_model.reactions.get_by_id(k).x * v for k, v in
                            objective_reaction_coefficient_dict.items()])
    return model.solution
Beispiel #13
0
def moma(wt_model, mutant_model, objective_sense='maximize', solver=None,
         tolerance_optimality=1e-8, tolerance_feasibility=1e-8,
         minimize_norm=True, norm_flux_dict=None, the_problem='return', lp_method=0,
         combined_model=None, norm_type='euclidean'):
    """Runs the minimization of metabolic adjustment method described in
    Segre et al 2002 PNAS 99(23): 15112-7.

    wt_model: A cobra.Model object
    mutant_model: A cobra.Model object with different reaction bounds vs wt_model.
    objective_sense: 'maximize' or 'minimize'
    solver: 'gurobi', 'cplex', or 'glpk'.  Note: glpk cannot be used with norm_type 'euclidean'
    tolerance_optimality: Solver tolerance for optimality.
    tolerance_feasibility: Solver tolerance for feasibility.
    the_problem: None or a problem object for the specific solver that can be
    used to hot start the next solution.
    lp_method: The method to use for solving the problem.  Depends on the solver.  See
    the cobra.flux_analysis.solvers.py file for more info.
        For norm_type == 'euclidean':
            the primal simplex works best for the test model (gurobi: lp_method=0, cplex: lp_method=1)
    combined_model: an output from moma that represents the combined optimization to be solved.

    """
    if solver is None:
        if norm_type == "euclidean":
            solver = get_solver_name(qp=True)
        else:
            solver = get_solver_name()  # linear is not even implemented yet
    if combined_model is not None or the_problem not in ['return']:
        warn("moma currently does not support reusing models or problems. " +\
             "continuing without them")
        combined_model = None
        the_problem = 'return'
    if solver.lower() == 'cplex' and lp_method == 0:
        #print 'for moma, solver method 0 is very slow for cplex. changing to method 1'
        lp_method = 1
    if solver.lower() == 'glpk' and norm_type == 'euclidean':
        try:
            # from gurobipy import Model
            solver = 'gurobi'
            warn("GLPK can't solve quadratic problems like MOMA.  Switched solver to %s"%solver)
        except:
            warn("GLPK can't solve quadratic problems like MOMA.  Switching to linear MOMA")

    if norm_type == 'euclidean':
        #Reusing the basis can get the solver stuck.
        reuse_basis = False
    if combined_model and combined_model.norm_type != norm_type:
        print('Cannot use combined_model.norm_type = %s with user-specified norm type'%(combined_model.norm_type,
                                                                                        norm_type))
        print('Defaulting to user-specified norm_type')
        combined_model = None


    if norm_type == 'linear':
        raise Exception('linear MOMA is not currently implmented')
        quadratic_component = None

    if minimize_norm:
        if norm_flux_dict is None:
            optimize_minimum_flux(wt_model, objective_sense='maximize',
                                  tolerance_feasibility=tolerance_feasibility)
            norm_flux_dict = wt_model.solution.x_dict

        else:
            # update the solution of wt model according to norm_flux_dict
            wt_model.optimize() # this is just to make sure wt_model.solution and mutant_model.solution refer to different object.
            objective_reaction_coefficient_dict = dict([(x.id, x.objective_coefficient)
                                                        for x in wt_model.reactions
                                                        if x.objective_coefficient])
            try:
                wt_model.solution.f = sum([norm_flux_dict[k] * v for k, v in
                                           objective_reaction_coefficient_dict.items()])
                wt_model.solution.x_dict = norm_flux_dict
            except:
                print('incorrect norm_flux_dict')
                raise

        # formulate qMOMA using wt_flux as reference
        # make a copy not to change the objective coefficients of original mutant model
        mutant_model_moma = mutant_model.copy()
        nRxns = len(mutant_model_moma.reactions)
        quadratic_component = 2 * eye(nRxns, nRxns)
        # linear component
        [setattr(x, 'objective_coefficient', -2 * norm_flux_dict[x.id]) for x in mutant_model_moma.reactions]

        the_problem = mutant_model_moma.optimize(objective_sense='minimize',
                                         quadratic_component=quadratic_component,
                                         solver=solver,
                                         tolerance_optimality=tolerance_optimality,
                                         tolerance_feasibility=tolerance_feasibility,
                                         lp_method=lp_method)
                                         #, reuse_basis=reuse_basis) # this should be commented out when solver is 'cplex'

        if mutant_model_moma.solution.status != 'optimal':
            warn('optimal moma solution not found: solver status %s'%mutant_model_moma.solution.status +\
                 ' returning the problem, the_combined model, and the quadratic component for trouble shooting')
            return(the_problem, mutant_model_moma, quadratic_component)

        solution = mutant_model_moma.solution
        mutant_dict = {}
        mutant_f = sum([mutant_model.reactions.get_by_id(x.id).objective_coefficient * x.x for x in mutant_model_moma.reactions])
        mutant_dict['objective_value'] = mutant_f
        mutant_dict['status'] = solution.status
        #TODO: Deal with maximize / minimize issues for a reversible model that's been converted to irreversible
        mutant_dict['flux_difference'] = flux_difference = sum([(norm_flux_dict[r.id] - mutant_model_moma.solution.x_dict[r.id])**2
                                                                for r in mutant_model_moma.reactions])
        mutant_dict['the_problem'] = the_problem
        mutant_dict['mutant_model'] = mutant_model_moma

        # update the solution of original mutant model
        mutant_model.solution.x_dict = the_problem.x_dict
        mutant_model.solution.status = solution.status
        mutant_model.solution.f = mutant_f

        # del wt_model, mutant_model, quadratic_component, solution
        return(mutant_dict)


    else:
        #Construct a problem that attempts to maximize the objective in the WT model while
        #solving the quadratic problem.  This new problem is constructed to try to find
        #a solution for the WT model that lies close to the mutant model.  There are
        #often multiple equivalent solutions with M matrices and the one returned
        #by a simple cobra_model.optimize call may be too far from the mutant.
        #This only needs to be adjusted if we update mutant_model._S after deleting reactions
        number_of_reactions_in_common = len(set([x.id for x in wt_model.reactions]).intersection([x.id for x in mutant_model.reactions]))
        number_of_reactions = len(wt_model.reactions) + len(mutant_model.reactions)
        #Get the optimal wt objective value and adjust based on optimality tolerances

        wt_model.optimize(solver=solver)
        wt_optimal = deepcopy(wt_model.solution.f)
        if objective_sense == 'maximize':
            wt_optimal = floor(wt_optimal/tolerance_optimality)*tolerance_optimality
        else:
            wt_optimal = ceil(wt_optimal/tolerance_optimality)*tolerance_optimality

        if not combined_model:
            #Collect the set of wt reactions contributing to the objective.
            objective_reaction_coefficient_dict = dict([(x.id, x.objective_coefficient)
                                                        for x in wt_model.reactions
                                                        if x.objective_coefficient])
            
            
            combined_model = construct_difference_model(wt_model, mutant_model, norm_type)
            #Add in the virtual objective metabolite to constrain the wt_model to the space where
            #the objective was maximal
            objective_metabolite = Metabolite('wt_optimal')
            objective_metabolite._bound = wt_optimal
            if objective_sense == 'maximize':
                objective_metabolite._constraint_sense = 'G'
            else:
                objective_metabolite._constraint_sense = 'L'

            #TODO: this couples the wt_model objective reaction to the virtual metabolite
            #Currently, assumes a single objective reaction; however, this may be extended
            [combined_model.reactions.get_by_id(k).add_metabolites({objective_metabolite: v})
             for k, v in objective_reaction_coefficient_dict.items()]
                

        if norm_type == 'euclidean':
            #Makes assumptions about the structure of combined model
            quadratic_component = s_vstack((lil_matrix((number_of_reactions, number_of_reactions + number_of_reactions_in_common )),
                                            s_hstack((lil_matrix((number_of_reactions_in_common, number_of_reactions)),
                                                      eye(number_of_reactions_in_common,number_of_reactions_in_common)))))
    
        elif norm_type == 'linear':
            quadratic_component = None

    combined_model.norm_type = norm_type
    cobra_model = combined_model

    the_problem = combined_model.optimize(objective_sense='minimize',
                                         quadratic_component=quadratic_component,
                                         solver=solver,
                                         tolerance_optimality=tolerance_optimality,
                                         tolerance_feasibility=tolerance_feasibility,
                                         lp_method=lp_method) #, reuse_basis=reuse_basis) # this should be commented out when solver is 'cplex'

    if combined_model.solution.status != 'optimal':
        warn('optimal moma solution not found: solver status %s'%combined_model.solution.status +\
             ' returning the problem, the_combined model, and the quadratic component for trouble shooting')
        return(the_problem, combined_model, quadratic_component)
             
    solution = combined_model.solution
    mutant_dict = {}
    #Might be faster to quey based on mutant_model.reactions with the 'mutant_' prefix added
    _reaction_list = [x for x in combined_model.reactions if x.id.startswith('mutant_')]
    mutant_f = sum([mutant_model.reactions.get_by_id(x.id[len('mutant_'):]).objective_coefficient *
                    x.x for x in _reaction_list])
    mutant_dict['objective_value'] = mutant_f
    wild_type_flux_total = sum([abs(solution.x_dict[x.id]) for x in wt_model.reactions])
    mutant_flux_total = sum(abs(x.x) for x in _reaction_list)
    #Need to use the new solution as there are multiple ways to achieve an optimal solution in
    #simulations with M matrices.
    mutant_dict['status'] = solution.status
    #TODO: Deal with maximize / minimize issues for a reversible model that's been converted to irreversible
    mutant_dict['flux_difference'] = flux_difference = sum([(solution.x_dict[x.id[len('mutant_'):]]
                                                             - x.x)**2 for x in _reaction_list])
    mutant_dict['the_problem'] = the_problem
    mutant_dict['combined_model'] = combined_model
    
    del wt_model, mutant_model, quadratic_component, solution
    return(mutant_dict)
Beispiel #14
0
	def min_incon_parsi_mwRange(self, metInterest, **kwargs):
		'''The main step called by compute_met_range to find the range for the molecular weight
		Return min_incon_parsi_info object summarizing the results of solving MIP.
			formulae: (formula for min MW, formula max MW)
			mw_range: (min MW, max MW)
			rhs: {e: {i: RHS[i] for all metabolite i}} the RHS value in the MIP problem for each element solved. (compute_met_range only)
			infeas: infeasibility of each solve
			bound: bound used for total inconsistency or the relaxation value eps0 for each solve
			obj: objective function value for each solve
			solution: solution values for each type of variables (m, xp, xn)
			met_model: the MIP problem solved for each element as a cobra model. Same model but different rhs for different elements.
			final: the final status of the solution
			sol_stat: solution status for each element/connected set of elements
		'''
		inf, neg_inf = self.infinity, self.negative_infinity
		pre = self.pre
		metK, metU, rxnK, ele, feasTol, digitRounded \
		= pre.met_known, pre.met_unknown, pre.rxn_known, pre.ele, pre.feasTol, pre.digitRounded
		model = self.model
		#handle metInterest
		if isinstance(metInterest,type(model.metabolites[0])):
			metI = metInterest
		elif isinstance(metInterest, str):
			metI = model.metabolites.get_by_id(metInterest)
		if metI in metK:
			print("%s in the input is already known." %metI.id)
			mip_info = min_incon_parsi_info()
			mip_info.mw_range = (metK[metI].mw, metK[metI].mw)
			return mip_info

		print('Find the range for the molecular weight of %s ... %s' %(metI.id, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))

		infeas, bound, solution, sol_stat, obj, rhs = ({} for i in range(6))
		for k in ['minIncon', 'minMw', 'maxMw']: #pre-assignment
			solution[k] = {'m': {}, 'xp': {}, 'xn': {}}
		ct = 0
		#The optimization problem for each element is the same except the RHS
		met_model = Model('min/max Mw of ' + metI.id)
		met_model.solver = self.__solver
		constraint = {j: Metabolite(j.id) for j in rxnK}
		m = {i: Reaction('m_' + i.id) for i in metU}
		xp = {j: Reaction('xp_' + j.id) for j in rxnK}
		xn = {j: Reaction('xn_' + j.id) for j in rxnK}
		for j in rxnK:
			xp[j].add_metabolites({constraint[j]: 1})
			xn[j].add_metabolites({constraint[j]: -1})
			xp[j].lower_bound, xn[j].lower_bound, xp[j].upper_bound, xn[j].upper_bound = 0, 0, inf, inf
			constraint[j]._constraint_sense = 'E'

		for i in metU:
			# S_ij x m_ie for each i
			m[i].add_metabolites({constraint[j]: j._metabolites[i] for j in list(i._reaction) if j in rxnK})
			m[i].upper_bound = inf

		#Add constraint to fix the total inconsistency for each element
		constraint_minIncon = Metabolite('minIncon')
		constraint_minIncon._bound = inf
		constraint_minIncon._constraint_sense = 'L'
		for j in rxnK:
			xp[j].add_metabolites({constraint_minIncon: 1})
			xn[j].add_metabolites({constraint_minIncon: 1})

		met_model.add_reactions([m[i] for i in metU])
		met_model.add_reactions([xp[j] for j in rxnK])
		met_model.add_reactions([xn[j] for j in rxnK])

		met_model.constraints[constraint_minIncon.id].lb = neg_inf #to avoid error

		objective_dict_minIncon = {xp[j]: 1 for j in rxnK}
		objective_dict_minIncon.update({xn[j]: 1 for j in rxnK})


		for e in ele:
			ct += 1
			print("Optimizing for %d / %d element ... %s" %(ct, len(ele), datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
			# metModelJ = Model('min/max ' + e)
			# metModelJ.solver = self.__solver
			infeasJ, boundJ, objJ, rhsJ = {}, {}, {}, {}
			# constraint = {j: Metabolite(j.id + ',' + e) for j in rxnK}
			# m = {i: Reaction('m_' + i.id + ',' + e) for i in metU}
			# xp = {j: Reaction('xp_' + j.id + ',' + e) for j in rxnK}
			# xn = {j: Reaction('xn_' + j.id + ',' + e) for j in rxnK}
			# for j in rxnK:
				#RHS for each constraint: -sum(S_ij * m^known_ie) (obsolete)
				# constraint[j]._bound = -sum([S_ij * metK[i].elements[e] for i, S_ij in j._metabolites.items() if i in metK and e in metK[i].elements])
				# constraint[j]._constraint_sense = 'E'
				#x_pos - x_neg for each constraint
				# xp[j].add_metabolites({constraint[j]: 1})
				# xn[j].add_metabolites({constraint[j]: -1})
				# xp[j].lower_bound, xn[j].lower_bound, xp[j].upper_bound, xn[j].upper_bound = 0, 0, inf, inf
			for i in metU:
				# # S_ij x m_ie for each i
				# m[i].add_metabolites({constraint[j]: j._metabolites[i] for j in list(i._reaction) if j in rxnK})
				# m[i].upper_bound = inf
				m[i].lower_bound = neg_inf if e == 'Charge' else 0
			#add reactions into the model
			# metModelJ.add_reactions([m[i] for i in metU])
			# metModelJ.add_reactions([xp[j] for j in rxnK])
			# metModelJ.add_reactions([xn[j] for j in rxnK])
			#set the objective function
			# objective_dict = {xp[j]: 1 for j in rxnK}
			# objective_dict.update({xn[j]: 1 for j in rxnK})
			met_model.constraints[constraint_minIncon.id].ub = inf
			set_objective(met_model, objective_dict_minIncon)
			#set RHS: -sum(S_ij * m^known_ie)
			for j in rxnK:
				met_model.constraints[constraint[j].id].ub = inf #ub must be set to be > lb to avoid error
				rhsJ[j] = -sum([S_ij * metK[i].elements[e] for i, S_ij in j._metabolites.items() if i in metK and e in metK[i].elements])
				met_model.constraints[constraint[j].id].lb, met_model.constraints[constraint[j].id].ub = rhsJ[j], rhsJ[j]
				constraint[j]._bound = rhsJ[j]

			#Solve for minimum inconsistency
			kwargs['objective_sense'] = 'minimize'
			sol = met_model.optimize(**kwargs)
			solStatJ = 'minIncon'
			infeasJ[solStatJ] = solution_infeasibility(met_model, sol)
			if sol.fluxes is None:
				boundJ[solStatJ] = float('nan')
			else:
				boundJ[solStatJ] = sum([sol.fluxes[j.id] for j in chain(xp.values(), xn.values())])
			if not infeasJ[solStatJ] <= feasTol:
				#infeasible (should not happen)
				infeasJ['minMw'], infeasJ['maxMw'] = inf, inf
				solStatJ = 'infeasible'
				objJ['minIncon'], objJ['minMw'], objJ['maxMw'] = (float('nan') for i in range(3))
				for s in ['minIncon','minMw','maxMw']:
					solution[s]['m'][e] = {i: float('nan') for i in metU}
					solution[s]['xp'][e] = {j: float('nan') for j in rxnK}
					solution[s]['xn'][e] = {j: float('nan') for j in rxnK}
			else:
				#Feasible. Store the solution
				solution[solStatJ]['m'][e] = {i: sol.fluxes[m[i].id] for i in metU}
				solution[solStatJ]['xp'][e] = {j: sol.fluxes[xp[j].id] for j in rxnK}
				solution[solStatJ]['xn'][e] = {j: sol.fluxes[xn[j].id] for j in rxnK}
				objJ[solStatJ] = sol.objective_value
				# #Add constraint to fix the total inconsistency for each element
				# constraint_minIncon = Metabolite('minIncon_'+e)
				# constraint_minIncon._bound = round(boundJ['minIncon'], digitRounded)
				# constraint_minIncon._constraint_sense = 'L'
				# for j in rxnK:
				# 	xp[j].add_metabolites({constraint_minIncon: 1})
				# 	xn[j].add_metabolites({constraint_minIncon: 1})

				# met_model.constraints[constraint_minIncon.id].lb = neg_inf #to avoid error
				met_model.constraints[constraint_minIncon.id].ub = round(boundJ['minIncon'], digitRounded)
				#reset the objective function to minimize molecular weight
				objective_dict = {m[metI]: Formula(formula=e).mw}
				set_objective(met_model, objective_dict)
				solStatJ = 'minMw'
				kwargs['objective_sense'] = 'minimize'
				eps0 = 1e-6
				while True:
					sol = met_model.optimize(**kwargs)
					infeasJ[solStatJ] = solution_infeasibility(met_model, sol)
					if infeasJ[solStatJ] <= feasTol or eps0 > 1e-4 + 1e-8:
						break
					eps0 *= 10
					#rounding to avoid infeasibility due to numerical issues
					met_model.constraints[constraint_minIncon.id].ub = round(boundJ['minIncon'] * (1 + eps0), digitRounded)

				boundJ[solStatJ] = eps0
				if infeasJ[solStatJ] <= feasTol:
					#Feasible. Store the solution
					solution[solStatJ]['m'].update({e: {i: sol.fluxes[m[i].id] for i in metU}})
					solution[solStatJ]['xp'].update({e: {j: sol.fluxes[xp[j].id] for j in rxnK}})
					solution[solStatJ]['xn'].update({e: {j: sol.fluxes[xn[j].id] for j in rxnK}})
					objJ[solStatJ] = sol.objective_value
				else:
					#infeasible, should not happen
					objJ[solStatJ] = float('nan')
					solution['minMw']['m'][e] = {i: float('nan') for i in metU}
					solution['minMw']['xp'][e] = {j: float('nan') for j in rxnK}
					solution['minMw']['xn'][e] = {j: float('nan') for j in rxnK}

				#maximize molecular weight
				solStatJ = 'maxMw'
				#reset the bound for total inconsistency
				met_model.constraints[constraint_minIncon.id].ub = round(boundJ['minIncon'], digitRounded)
				kwargs['objective_sense'] = 'maximize'
				eps0 = 1e-6
				while True:
					sol = met_model.optimize(**kwargs)
					infeasJ[solStatJ] = solution_infeasibility(met_model, sol)
					if infeasJ[solStatJ] <= feasTol or eps0 > 1e-4 + 1e-8:
						break
					eps0 *= 10
					#rounding to avoid infeasibility due to numerical issues
					met_model.constraints[constraint_minIncon.id].ub = round(boundJ['minIncon'] * (1 + eps0), digitRounded)

				boundJ[solStatJ] = eps0
				if infeasJ[solStatJ] <= feasTol:
					#Feasible. Store the solution
					solution[solStatJ]['m'].update({e: {i: sol.fluxes[m[i].id] for i in metU}})
					solution[solStatJ]['xp'].update({e: {j: sol.fluxes[xp[j].id] for j in rxnK}})
					solution[solStatJ]['xn'].update({e: {j: sol.fluxes[xn[j].id] for j in rxnK}})
					objJ[solStatJ] = sol.objective_value
				else:
					#infeasible, should not happen
					objJ[solStatJ] = float('nan')
					solution['maxMw']['m'][e] = {i: float('nan') for i in metU}
					solution['maxMw']['xp'][e] = {j: float('nan') for j in rxnK}
					solution['maxMw']['xn'][e] = {j: float('nan') for j in rxnK}

			#store data
			infeas[e] = infeasJ
			bound[e] = boundJ
			obj[e] = objJ
			# met_model[e] = met_model
			sol_stat[e] = solStatJ
			rhs[e] = rhsJ

		#summarize the final solution state
		if any([k == 'infeasible' for k in sol_stat.values()]):
			print('Failure: no feasible solution can be found.')
			solFinal = 'infeasible'
		else:
			if all([obj[e]['minMw'] == obj[e]['minMw'] for e in ele]):
				if all([obj[e]['maxMw'] == obj[e]['maxMw'] for e in ele]):
					solFinal = 'minMw + maxMw'
				else:
					solFinal = 'minMw only'
			else:
				if all([obj[e]['maxMw'] == obj[e]['maxMw'] for e in ele]):
					solFinal = 'maxMw only'
				else:
					solFinal = 'minIncon'

		mip_info = min_incon_parsi_info()
		mip_info.infeas, mip_info.bound, mip_info.obj, mip_info.solution, \
		mip_info.met_model, mip_info.final, mip_info.sol_stat \
		= infeas, bound, obj, solution, met_model, solFinal, sol_stat
		mip_info.rhs = rhs
		mip_info.mw_range = (sum([obj[e]['minMw'] for e in ele]), sum([obj[e]['maxMw'] for e in ele]))
		mip_info.formulae = tuple([formula_dict2str({e: solution[s]['m'][e][metI] for e in ele}) for s in ['minMw', 'maxMw']])
		return mip_info