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_iadd(model: Model) -> None: """Test in-place addition of reaction.""" PGI = model.reactions.PGI EX_h2o = model.reactions.EX_h2o_e original_PGI_gpr = PGI.gene_reaction_rule PGI += EX_h2o assert PGI.gene_reaction_rule == original_PGI_gpr assert PGI.metabolites[model.metabolites.h2o_e] == -1.0 # Original should not change assert EX_h2o.gene_reaction_rule == "" assert EX_h2o.metabolites[model.metabolites.h2o_e] == -1.0 # Add a reaction not in the model new_reaction = Reaction("test") new_reaction.add_metabolites({Metabolite("A"): -1, Metabolite("B"): 1}) PGI += new_reaction assert PGI.gene_reaction_rule == original_PGI_gpr assert len(PGI.gene_reaction_rule) == 5 # And vice versa new_reaction += PGI assert len(new_reaction.metabolites) == 5 # not assert len(new_reaction.genes) == 1 assert new_reaction.gene_reaction_rule == original_PGI_gpr # Combine two GPRs model.reactions.ACKr += model.reactions.ACONTa expected_rule = "(b2296 or b3115 or b1849) and (b0118 or b1276)" assert model.reactions.ACKr.gene_reaction_rule == expected_rule assert len(model.reactions.ACKr.genes) == 5
def construct_geometric_fba_model(): test_model = Model('geometric_fba_paper_model') test_model.add_metabolites(Metabolite('A')) test_model.add_metabolites(Metabolite('B')) v1 = Reaction('v1', upper_bound=1.0) v1.add_metabolites({test_model.metabolites.A: 1.0}) v2 = Reaction('v2', lower_bound=-1000.0) v2.add_metabolites({ test_model.metabolites.A: -1.0, test_model.metabolites.B: 1.0 }) v3 = Reaction('v3', lower_bound=-1000.0) v3.add_metabolites({ test_model.metabolites.A: -1.0, test_model.metabolites.B: 1.0 }) v4 = Reaction('v4', lower_bound=-1000.0) v4.add_metabolites({ test_model.metabolites.A: -1.0, test_model.metabolites.B: 1.0 }) v5 = Reaction('v5') v5.add_metabolites({ test_model.metabolites.A: 0.0, test_model.metabolites.B: -1.0 }) test_model.add_reactions([v1, v2, v3, v4, v5]) test_model.objective = 'v5' return test_model
def test_add_reaction_context(self, model): old_reaction_count = len(model.reactions) old_metabolite_count = len(model.metabolites) dummy_metabolite_1 = Metabolite("test_foo_1") dummy_metabolite_2 = Metabolite("test_foo_2") actual_metabolite = model.metabolites[0] copy_metabolite = model.metabolites[1].copy() dummy_reaction = Reaction("test_foo_reaction") dummy_reaction.add_metabolites({ dummy_metabolite_1: -1, dummy_metabolite_2: 1, copy_metabolite: -2, actual_metabolite: 1 }) dummy_reaction.gene_reaction_rule = 'dummy_gene' with model: model.add_reaction(dummy_reaction) assert model.reactions.get_by_id(dummy_reaction.id) == \ dummy_reaction assert len(model.reactions) == old_reaction_count + 1 assert len(model.metabolites) == old_metabolite_count + 2 assert dummy_metabolite_1._model == model assert 'dummy_gene' in model.genes assert len(model.reactions) == old_reaction_count assert len(model.metabolites) == old_metabolite_count with pytest.raises(KeyError): model.reactions.get_by_id(dummy_reaction.id) assert dummy_metabolite_1._model is None assert 'dummy_gene' not in model.genes
def test_iadd(self, model): PGI = model.reactions.PGI EX_h2o = model.reactions.EX_h2o_e original_PGI_gpr = PGI.gene_reaction_rule PGI += EX_h2o assert PGI.gene_reaction_rule == original_PGI_gpr assert PGI.metabolites[model.metabolites.h2o_e] == -1.0 # original should not have changed assert EX_h2o.gene_reaction_rule == '' assert EX_h2o.metabolites[model.metabolites.h2o_e] == -1.0 # what about adding a reaction not in the model new_reaction = Reaction("test") new_reaction.add_metabolites({Metabolite("A"): -1, Metabolite("B"): 1}) PGI += new_reaction assert PGI.gene_reaction_rule == original_PGI_gpr assert len(PGI.gene_reaction_rule) == 5 # and vice versa new_reaction += PGI assert len(new_reaction.metabolites) == 5 # not assert len(new_reaction.genes) == 1 assert new_reaction.gene_reaction_rule == original_PGI_gpr # what about combining 2 gpr's model.reactions.ACKr += model.reactions.ACONTa expected_rule = '(b2296 or b3115 or b1849) and (b0118 or b1276)' assert model.reactions.ACKr.gene_reaction_rule == expected_rule assert len(model.reactions.ACKr.genes) == 5
def test_get_yield_not_working_if_input_formula_missing(self): model = Model("test") met1 = Metabolite(id="S7P") met2 = Metabolite(id="T3P1", formula="C3H7O6P") met3 = Metabolite(id="E4P", formula="C4H9O7P") met4 = Metabolite(id="F6P", formula="C6H13O9P") model.add_metabolites((met1, met2, met3, met4)) react1 = Reaction(id="r1", name="Transaldolase") react1.add_metabolites({met1: -1, met2: -1, met3: 1, met4: 1}) react2 = Reaction(id="r2") react2.add_metabolites({met1: -1}) react3 = Reaction(id="r3") react3.add_metabolites({met2: -1}) react4 = Reaction(id="r4") react4.add_metabolites({met3: -1}) react5 = Reaction(id="r5") react5.add_metabolites({met4: -1}) model.add_reactions((react1, react2, react3, react4, react5)) fluxes = { react1.id: 1, react2.id: -1, react3.id: -1, react4.id: 1, react5.id: 1 } status, _ = get_yields(fluxes, model) assert status is False
def test_add_reactions(self, model): r1 = Reaction('r1') r1.add_metabolites({Metabolite('A'): -1, Metabolite('B'): 1}) r1.lower_bound, r1.upper_bound = -999999., 999999. r2 = Reaction('r2') r2.add_metabolites({ Metabolite('A'): -1, Metabolite('C'): 1, Metabolite('D'): 1 }) r2.lower_bound, r2.upper_bound = 0., 999999. model.add_reactions([r1, r2]) r2.objective_coefficient = 3. assert r2.objective_coefficient == 3. assert model.reactions[-2] == r1 assert model.reactions[-1] == r2 assert isinstance(model.reactions[-2].reverse_variable, model.problem.Variable) coefficients_dict = model.objective.expression. \ as_coefficients_dict() biomass_r = model.reactions.get_by_id('Biomass_Ecoli_core') assert coefficients_dict[biomass_r.forward_variable] == 1. assert coefficients_dict[biomass_r.reverse_variable] == -1. assert coefficients_dict[model.reactions.r2.forward_variable] == 3. assert coefficients_dict[model.reactions.r2.reverse_variable] == -3.
def construct_ll_test_model(): test_model = Model() test_model.add_metabolites(Metabolite("A")) test_model.add_metabolites(Metabolite("B")) test_model.add_metabolites(Metabolite("C")) EX_A = Reaction("EX_A") EX_A.add_metabolites({test_model.metabolites.A: 1}) DM_C = Reaction("DM_C") DM_C.add_metabolites({test_model.metabolites.C: -1}) v1 = Reaction("v1") v1.add_metabolites({ test_model.metabolites.A: -1, test_model.metabolites.B: 1 }) v2 = Reaction("v2") v2.add_metabolites({ test_model.metabolites.B: -1, test_model.metabolites.C: 1 }) v3 = Reaction("v3") v3.add_metabolites({ test_model.metabolites.C: -1, test_model.metabolites.A: 1 }) test_model.add_reactions([EX_A, DM_C, v1, v2, v3]) DM_C.objective_coefficient = 1 return test_model
def test_assess(self, model, solver): with model: assert assess(model, model.reactions.GLCpts, solver=solver) is True pyr = model.metabolites.pyr_c a = Metabolite('a') b = Metabolite('b') model.add_metabolites([a, b]) pyr_a2b = Reaction('pyr_a2b') pyr_a2b.add_metabolites({pyr: -1, a: -1, b: 1}) model.add_reactions([pyr_a2b]) res = assess(model, pyr_a2b, 0.01, solver=solver) expected = { 'precursors': { a: { 'required': 0.01, 'produced': 0.0 } }, 'products': { b: { 'required': 0.01, 'capacity': 0.0 } } } assert res == expected
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
def test_add_reactions_single_existing(model): rxn = model.reactions[0] r1 = Reaction(rxn.id) r1.add_metabolites({Metabolite("A"): -1, Metabolite("B"): 1}) r1.lower_bound, r1.upper_bound = -999999.0, 999999.0 model.add_reactions([r1]) assert rxn in model.reactions assert r1 is not model.reactions.get_by_id(rxn.id)
def test_bad_exchange(model: Model) -> None: """Test bad exchange reaction identification.""" with pytest.raises(ValueError): m = Metabolite("baddy", compartment="nonsense") model.add_boundary(m, type="exchange") m = Metabolite("goody", compartment="e") rxn = model.add_boundary(m, type="exchange") assert isinstance(rxn, Reaction)
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
def test__normalize_pseudoreaction_atpm(): reaction = Reaction('notATPM') reaction.add_metabolites({Metabolite('atp_c'): -1, Metabolite('h2o_c'): -1, Metabolite('pi_c'): 1, Metabolite('h_c'): 1, Metabolite('adp_c'): 1}) _normalize_pseudoreaction(reaction) assert reaction.id == 'ATPM' assert reaction.subsystem == 'Biomass and maintenance functions'
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
def test__normalize_pseudoreaction_atpm_has_gpr(): reaction = Reaction('NPT1') reaction.add_metabolites({Metabolite('atp_c'): -1, Metabolite('h2o_c'): -1, Metabolite('pi_c'): 1, Metabolite('h_c'): 1, Metabolite('adp_c'): 1}) reaction.gene_reaction_rule = 'b1779' _normalize_pseudoreaction(reaction) # should not change assert reaction.id == 'NPT1'
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)
def test_set_id(self, solved_model): solution, model = solved_model met = Metabolite("test") with pytest.raises(TypeError): setattr(met, 'id', 1) model.add_metabolites([met]) with pytest.raises(ValueError): setattr(met, "id", 'g6p_c') met.id = "test2" assert "test2" in model.metabolites assert "test" not in model.metabolites
def test_compartments(self, model): assert set(model.compartments) == {"c", "e"} model = Model("test", "test") met_c = Metabolite("a_c", compartment="c") met_e = Metabolite("a_e", compartment="e") rxn = Reaction("foo") rxn.add_metabolites({met_e: -1, met_c: 1}) model.add_reactions([rxn]) assert model.compartments == {'c': '', 'e': ''} model.compartments = {'c': 'cytosol'} assert model.compartments == {'c': 'cytosol', 'e': ''}
def test_get_yield(self): model = Model("test") met1 = Metabolite(id="S7P", formula="C7H15O10P") met2 = Metabolite(id="T3P1", formula="C3H7O6P") met3 = Metabolite(id="E4P", formula="C4H9O7P") met4 = Metabolite(id="F6P", formula="C6H13O9P") model.add_metabolites((met1, met2, met3, met4)) react1 = Reaction(id="r1", name="Transaldolase") react1.add_metabolites({met1: -1, met2: -1, met3: 1, met4: 1}) react2 = Reaction(id="r2") react2.add_metabolites({met1: -1}) react3 = Reaction(id="r3") react3.add_metabolites({met2: -1}) react4 = Reaction(id="r4") react4.add_metabolites({met3: -1}) react5 = Reaction(id="r5") react5.add_metabolites({met4: -1}) model.add_reactions((react1, react2, react3, react4, react5)) fluxes = { react1.id: 1, react2.id: -1, react3.id: -1, react4.id: 1, react5.id: 1 } status, yields = get_yields(fluxes, model) assert status is True assert yields == { "C": { met3: 0.4, met4: 0.6 }, "H": { met3: 9 / 22, met4: 13 / 22 }, "O": { met3: 7 / 16, met4: 9 / 16 }, "P": { met3: 0.5, met4: 0.5 } }
def test_add_reactions_duplicate(model): rxn = model.reactions[0] r1 = Reaction('r1') r1.add_metabolites({Metabolite('A'): -1, Metabolite('B'): 1}) r1.lower_bound, r1.upper_bound = -999999., 999999. r2 = Reaction(rxn.id) r2.add_metabolites( {Metabolite('A'): -1, Metabolite('C'): 1, Metabolite('D'): 1}) model.add_reactions([r1, r2]) assert r1 in model.reactions assert rxn in model.reactions assert r2 is not model.reactions.get_by_id(rxn.id)
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
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 test__normalize_pseudoreaction_atpm_reversed(): reaction = Reaction('notATPM') reaction.add_metabolites({Metabolite('atp_c'): 1, Metabolite('h2o_c'): 1, Metabolite('pi_c'): -1, Metabolite('h_c'): -1, Metabolite('adp_c'): -1}) reaction.lower_bound = -50 reaction.upper_bound = 100 _normalize_pseudoreaction(reaction) assert reaction.id == 'ATPM' assert reaction.lower_bound == -100 assert reaction.upper_bound == 50
def test_add_metabolite(self, model): with pytest.raises(ValueError): model.add_metabolites(Metabolite()) with model: with model: reaction = model.reactions.get_by_id("PGI") reaction.add_metabolites({model.metabolites[0]: 1}) assert model.metabolites[0] in reaction._metabolites fake_metabolite = Metabolite("fake") reaction.add_metabolites({fake_metabolite: 1}) assert fake_metabolite in reaction._metabolites assert model.metabolites.has_id("fake") assert model.metabolites.get_by_id("fake") is fake_metabolite assert len(model._contexts[0]._history) == 0 assert fake_metabolite._model is None assert fake_metabolite not in reaction._metabolites assert "fake" not in model.metabolites # test adding by string with model: reaction.add_metabolites({"g6p_c": -1}) # already in reaction assert reaction._metabolites[model.metabolites.get_by_id( "g6p_c")] == -2 reaction.add_metabolites({"h_c": 1}) assert reaction._metabolites[model.metabolites.get_by_id( "h_c")] == 1 with pytest.raises(KeyError): reaction.add_metabolites({"missing": 1}) assert reaction._metabolites[model.metabolites.get_by_id( "g6p_c")] == -1 assert model.metabolites.h_c not in reaction._metabolites # Test combine=False reaction = model.reactions.get_by_id("ATPM") old_stoich = reaction._metabolites[model.metabolites.get_by_id( "h2o_c")] with model: reaction.add_metabolites({'h2o_c': 2.5}, combine=False) assert reaction._metabolites[model.metabolites.get_by_id( "h2o_c")] == 2.5 assert reaction._metabolites[model.metabolites.get_by_id( "h2o_c")] == old_stoich # test adding to a new Reaction reaction = Reaction("test") assert len(reaction._metabolites) == 0 reaction.add_metabolites({Metabolite("test_met"): -1}) assert len(reaction._metabolites) == 1
def construct_difference_model(model_1, model_2, norm_type='euclidean'): """Combine two models into a larger model that is designed to calculate differences between the models """ #Get index mappings common_dict = {} #Using copies of the models so things are modified above combined_model = model_1 = model_1.copy() model_2 = model_2.copy() for reaction_1 in model_1.reactions: try: reaction_2 = model_2.reactions.get_by_id(reaction_1.id) common_dict[reaction_1] = reaction_2 except: continue #Add a prefix in front of the mutant_model metabolites and reactions to prevent #name collisions in DictList for the_dict_list in [model_2.metabolites, model_2.reactions]: [setattr(x, 'id', 'mutant_%s'%x.id) for x in the_dict_list] the_dict_list._generate_index() #Update the DictList.dicts combined_model.add_reactions(model_2.reactions) [setattr(x, 'objective_coefficient', 0.) for x in combined_model.reactions] #Add in the difference reactions. The mutant reactions and metabolites are already added. #This must be a list to maintain the correct order when adding the difference_metabolites difference_reactions = [] #Add the difference reactions at the end to speed things up difference_metabolites = [] for reaction_1, reaction_2 in iteritems(common_dict): reaction_1._difference_partner = reaction_2 reaction_2._difference_partner = reaction_1 difference_reaction = Reaction('difference_%s'%reaction_1.id) difference_reactions.append(difference_reaction) difference_reaction.upper_bound = 100000 difference_reaction.lower_bound = -1* difference_reaction.upper_bound difference_metabolite = Metabolite('difference_%s'%reaction_1.id) difference_metabolites.append(difference_metabolite) if norm_type == 'linear': difference_metabolite._constraint_sense = 'G' reaction_1.add_metabolites({difference_metabolite: -1.}, add_to_container_model=False) reaction_2.add_metabolites({difference_metabolite: 1.}, add_to_container_model=False) difference_reaction.add_metabolites({difference_metabolite: 1.}, add_to_container_model=False) combined_model.add_metabolites(difference_metabolites) combined_model.add_reactions(difference_reactions) return(combined_model)
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
def geometric_fba_model(): """ Generate geometric FBA model as described in [1]_ References ---------- .. [1] Smallbone, Kieran & Simeonidis, Vangelis. (2009). Flux balance analysis: A geometric perspective. Journal of theoretical biology.258. 311-5. 10.1016/j.jtbi.2009.01.027. """ test_model = Model("geometric_fba_paper_model") test_model.add_metabolites(Metabolite("A")) test_model.add_metabolites(Metabolite("B")) v_1 = Reaction("v1", upper_bound=1.0) v_1.add_metabolites({test_model.metabolites.A: 1.0}) v_2 = Reaction("v2", lower_bound=-1000.0) v_2.add_metabolites({ test_model.metabolites.A: -1.0, test_model.metabolites.B: 1.0 }) v_3 = Reaction("v3", lower_bound=-1000.0) v_3.add_metabolites({ test_model.metabolites.A: -1.0, test_model.metabolites.B: 1.0 }) v_4 = Reaction("v4", lower_bound=-1000.0) v_4.add_metabolites({ test_model.metabolites.A: -1.0, test_model.metabolites.B: 1.0 }) v_5 = Reaction("v5") v_5.add_metabolites({ test_model.metabolites.A: 0.0, test_model.metabolites.B: -1.0 }) test_model.add_reactions([v_1, v_2, v_3, v_4, v_5]) test_model.objective = "v5" return test_model
def test_add_reactions_duplicate(model): rxn = model.reactions[0] r1 = Reaction("r1") r1.add_metabolites({Metabolite("A"): -1, Metabolite("B"): 1}) r1.lower_bound, r1.upper_bound = -999999.0, 999999.0 r2 = Reaction(rxn.id) r2.add_metabolites({ Metabolite("A"): -1, Metabolite("C"): 1, Metabolite("D"): 1 }) model.add_reactions([r1, r2]) assert r1 in model.reactions assert rxn in model.reactions assert r2 is not model.reactions.get_by_id(rxn.id)
def test_complicated_model(): """Test a complicated model. Difficult model since the online mean calculation is numerically unstable so many samples weakly violate the equality constraints. """ model = Model('flux_split') reaction1 = Reaction('V1') reaction2 = Reaction('V2') reaction3 = Reaction('V3') reaction1.bounds = (0, 6) reaction2.bounds = (0, 8) reaction3.bounds = (0, 10) A = Metabolite('A') reaction1.add_metabolites({A: -1}) reaction2.add_metabolites({A: -1}) reaction3.add_metabolites({A: 1}) model.add_reactions([reaction1, reaction2, reaction3]) optgp = OptGPSampler(model, 1, seed=42) achr = ACHRSampler(model, seed=42) optgp_samples = optgp.sample(100) achr_samples = achr.sample(100) assert any(optgp_samples.corr().abs() < 1.0) assert any(achr_samples.corr().abs() < 1.0) # > 95% are valid assert sum(optgp.validate(optgp_samples) == "v") > 95 assert sum(achr.validate(achr_samples) == "v") > 95
def test_model_remove_reaction(model): old_reaction_count = len(model.reactions) with model: model.remove_reactions(["PGI"]) assert len(model.reactions) == old_reaction_count - 1 with pytest.raises(KeyError): model.reactions.get_by_id("PGI") model.remove_reactions(model.reactions[:1]) assert len(model.reactions) == old_reaction_count - 2 assert len(model.reactions) == old_reaction_count assert "PGI" in model.reactions tmp_metabolite = Metabolite("testing") model.reactions[0].add_metabolites({tmp_metabolite: 1}) assert tmp_metabolite in model.metabolites model.remove_reactions(model.reactions[:1], remove_orphans=True) assert tmp_metabolite not in model.metabolites with model: model.reactions[0].add_metabolites({tmp_metabolite: 1}) assert tmp_metabolite in model.metabolites assert tmp_metabolite not in model.metabolites biomass_before = model.slim_optimize() with model: model.remove_reactions([model.reactions.Biomass_Ecoli_core]) assert np.isclose(model.slim_optimize(), 0) assert np.isclose(model.slim_optimize(), biomass_before)
def test_reaction_delete(self, model): old_reaction_count = len(model.reactions) tmp_metabolite = Metabolite("testing") # Delete without removing orphan model.reactions[0].add_metabolites({tmp_metabolite: 1}) assert len(tmp_metabolite.reactions) == 1 model.reactions[0].delete(remove_orphans=False) # make sure it's still in the model assert tmp_metabolite in model.metabolites assert len(tmp_metabolite.reactions) == 0 assert len(model.reactions) == old_reaction_count - 1 # Now try it with removing orphans model.reactions[0].add_metabolites({tmp_metabolite: 1}) assert len(tmp_metabolite.reactions) == 1 model.reactions[0].delete(remove_orphans=True) assert tmp_metabolite not in model.metabolites assert len(tmp_metabolite.reactions) == 0 assert len(model.reactions) == old_reaction_count - 2 # It shouldn't remove orphans if it's in 2 reactions however model.reactions[0].add_metabolites({tmp_metabolite: 1}) model.reactions[1].add_metabolites({tmp_metabolite: 1}) assert len(tmp_metabolite.reactions) == 2 model.reactions[0].delete(remove_orphans=False) assert tmp_metabolite in model.metabolites assert len(tmp_metabolite.reactions) == 1 assert len(model.reactions) == old_reaction_count - 3
def test_complicated_model(self): """Difficult model since the online mean calculation is numerically unstable so many samples weakly violate the equality constraints.""" model = Model('flux_split') reaction1 = Reaction('V1') reaction2 = Reaction('V2') reaction3 = Reaction('V3') reaction1.lower_bound = 0 reaction2.lower_bound = 0 reaction3.lower_bound = 0 reaction1.upper_bound = 6 reaction2.upper_bound = 8 reaction3.upper_bound = 10 A = Metabolite('A') reaction1.add_metabolites({A: -1}) reaction2.add_metabolites({A: -1}) reaction3.add_metabolites({A: 1}) model.add_reactions([reaction1]) model.add_reactions([reaction2]) model.add_reactions([reaction3]) optgp = OptGPSampler(model, 1, seed=42) achr = ACHRSampler(model, seed=42) optgp_samples = optgp.sample(100) achr_samples = achr.sample(100) assert any(optgp_samples.corr().abs() < 1.0) assert any(achr_samples.corr().abs() < 1.0) # > 95% are valid assert (sum(optgp.validate(optgp_samples) == "v") > 95) assert (sum(achr.validate(achr_samples) == "v") > 95)
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)
def test_metabolite_formula(): met = Metabolite("water") met.formula = "H2O" assert met.elements == {"H": 2, "O": 1} assert met.formula_weight == 18.01528
def from_mat_struct(mat_struct, model_id=None, inf=inf): """create a model from the COBRA toolbox struct The struct will be a dict read in by scipy.io.loadmat """ m = mat_struct if m.dtype.names is None: raise ValueError("not a valid mat struct") if not {"rxns", "mets", "S", "lb", "ub"} <= set(m.dtype.names): raise ValueError("not a valid mat struct") if "c" in m.dtype.names: c_vec = m["c"][0, 0] else: c_vec = None warn("objective vector 'c' not found") model = Model() if model_id is not None: model.id = model_id elif "description" in m.dtype.names: description = m["description"][0, 0][0] if not isinstance(description, string_types) and len(description) > 1: model.id = description[0] warn("Several IDs detected, only using the first.") else: model.id = description else: model.id = "imported_model" for i, name in enumerate(m["mets"][0, 0]): new_metabolite = Metabolite() new_metabolite.id = str(name[0][0]) if all(var in m.dtype.names for var in ['metComps', 'comps', 'compNames']): comp_index = m["metComps"][0, 0][i][0] - 1 new_metabolite.compartment = m['comps'][0, 0][comp_index][0][0] if new_metabolite.compartment not in model.compartments: comp_name = m['compNames'][0, 0][comp_index][0][0] model.compartments[new_metabolite.compartment] = comp_name else: new_metabolite.compartment = _get_id_compartment(new_metabolite.id) if new_metabolite.compartment not in model.compartments: model.compartments[ new_metabolite.compartment] = new_metabolite.compartment try: new_metabolite.name = str(m["metNames"][0, 0][i][0][0]) except (IndexError, ValueError): pass try: new_metabolite.formula = str(m["metFormulas"][0][0][i][0][0]) except (IndexError, ValueError): pass try: new_metabolite.charge = float(m["metCharge"][0, 0][i][0]) int_charge = int(new_metabolite.charge) if new_metabolite.charge == int_charge: new_metabolite.charge = int_charge except (IndexError, ValueError): pass model.add_metabolites([new_metabolite]) new_reactions = [] coefficients = {} for i, name in enumerate(m["rxns"][0, 0]): new_reaction = Reaction() new_reaction.id = str(name[0][0]) new_reaction.lower_bound = float(m["lb"][0, 0][i][0]) new_reaction.upper_bound = float(m["ub"][0, 0][i][0]) if isinf(new_reaction.lower_bound) and new_reaction.lower_bound < 0: new_reaction.lower_bound = -inf if isinf(new_reaction.upper_bound) and new_reaction.upper_bound > 0: new_reaction.upper_bound = inf if c_vec is not None: coefficients[new_reaction] = float(c_vec[i][0]) try: new_reaction.gene_reaction_rule = str(m['grRules'][0, 0][i][0][0]) except (IndexError, ValueError): pass try: new_reaction.name = str(m["rxnNames"][0, 0][i][0][0]) except (IndexError, ValueError): pass try: new_reaction.subsystem = str(m['subSystems'][0, 0][i][0][0]) except (IndexError, ValueError): pass new_reactions.append(new_reaction) model.add_reactions(new_reactions) set_objective(model, coefficients) coo = scipy_sparse.coo_matrix(m["S"][0, 0]) for i, j, v in zip(coo.row, coo.col, coo.data): model.reactions[j].add_metabolites({model.metabolites[i]: v}) return model
def create_cobra_model_from_sbml_file(sbml_filename, old_sbml=False, legacy_metabolite=False, print_time=False, use_hyphens=False): """convert an SBML XML file into a cobra.Model object. Supports SBML Level 2 Versions 1 and 4. The function will detect if the SBML fbc package is used in the file and run the converter if the fbc package is used. Parameters ---------- sbml_filename: string old_sbml: bool Set to True if the XML file has metabolite formula appended to metabolite names. This was a poorly designed artifact that persists in some models. legacy_metabolite: bool If True then assume that the metabolite id has the compartment id appended after an underscore (e.g. _c for cytosol). This has not been implemented but will be soon. print_time: bool deprecated use_hyphens: bool If True, double underscores (__) in an SBML ID will be converted to hyphens Returns ------- Model : The parsed cobra model """ if not libsbml: raise ImportError('create_cobra_model_from_sbml_file ' 'requires python-libsbml') __default_lower_bound = -1000 __default_upper_bound = 1000 __default_objective_coefficient = 0 # Ensure that the file exists if not isfile(sbml_filename): raise IOError('Your SBML file is not found: %s' % sbml_filename) # Expressions to change SBML Ids to Palsson Lab Ids metabolite_re = re.compile('^M_') reaction_re = re.compile('^R_') compartment_re = re.compile('^C_') if print_time: warn("print_time is deprecated", DeprecationWarning) model_doc = libsbml.readSBML(sbml_filename) if model_doc.getPlugin("fbc") is not None: from libsbml import ConversionProperties, LIBSBML_OPERATION_SUCCESS conversion_properties = ConversionProperties() conversion_properties.addOption( "convert fbc to cobra", True, "Convert FBC model to Cobra model") result = model_doc.convert(conversion_properties) if result != LIBSBML_OPERATION_SUCCESS: raise Exception("Conversion of SBML+fbc to COBRA failed") sbml_model = model_doc.getModel() sbml_model_id = sbml_model.getId() sbml_species = sbml_model.getListOfSpecies() sbml_reactions = sbml_model.getListOfReactions() sbml_compartments = sbml_model.getListOfCompartments() compartment_dict = dict([(compartment_re.split(x.getId())[-1], x.getName()) for x in sbml_compartments]) if legacy_metabolite: # Deal with the palsson lab appending the compartment id to the # metabolite id new_dict = {} for the_id, the_name in compartment_dict.items(): if the_name == '': new_dict[the_id[0].lower()] = the_id else: new_dict[the_id] = the_name compartment_dict = new_dict legacy_compartment_converter = dict( [(v, k) for k, v in iteritems(compartment_dict)]) cobra_model = Model(sbml_model_id) metabolites = [] metabolite_dict = {} # Convert sbml_metabolites to cobra.Metabolites for sbml_metabolite in sbml_species: # Skip sbml boundary species if sbml_metabolite.getBoundaryCondition(): continue if (old_sbml or legacy_metabolite) and \ sbml_metabolite.getId().endswith('_b'): # Deal with incorrect sbml from bigg.ucsd.edu continue tmp_metabolite = Metabolite() metabolite_id = tmp_metabolite.id = sbml_metabolite.getId() tmp_metabolite.compartment = compartment_re.split( sbml_metabolite.getCompartment())[-1] if legacy_metabolite: if tmp_metabolite.compartment not in compartment_dict: tmp_metabolite.compartment = legacy_compartment_converter[ tmp_metabolite.compartment] tmp_metabolite.id = parse_legacy_id( tmp_metabolite.id, tmp_metabolite.compartment, use_hyphens=use_hyphens) if use_hyphens: tmp_metabolite.id = metabolite_re.split( tmp_metabolite.id)[-1].replace('__', '-') else: # Just in case the SBML ids are ill-formed and use - tmp_metabolite.id = metabolite_re.split( tmp_metabolite.id)[-1].replace('-', '__') tmp_metabolite.name = sbml_metabolite.getName() tmp_formula = '' tmp_metabolite.notes = parse_legacy_sbml_notes( sbml_metabolite.getNotesString()) if sbml_metabolite.isSetCharge(): tmp_metabolite.charge = sbml_metabolite.getCharge() if "CHARGE" in tmp_metabolite.notes: note_charge = tmp_metabolite.notes["CHARGE"][0] try: note_charge = float(note_charge) if note_charge == int(note_charge): note_charge = int(note_charge) except: warn("charge of %s is not a number (%s)" % (tmp_metabolite.id, str(note_charge))) else: if ((tmp_metabolite.charge is None) or (tmp_metabolite.charge == note_charge)): tmp_metabolite.notes.pop("CHARGE") # set charge to the one from notes if not assigend before # the same tmp_metabolite.charge = note_charge else: # tmp_metabolite.charge != note_charge msg = "different charges specified for %s (%d and %d)" msg = msg % (tmp_metabolite.id, tmp_metabolite.charge, note_charge) warn(msg) # Chances are a 0 note charge was written by mistake. We # will default to the note_charge in this case. if tmp_metabolite.charge == 0: tmp_metabolite.charge = note_charge for the_key in tmp_metabolite.notes.keys(): if the_key.lower() == 'formula': tmp_formula = tmp_metabolite.notes.pop(the_key)[0] break if tmp_formula == '' and old_sbml: tmp_formula = tmp_metabolite.name.split('_')[-1] tmp_metabolite.name = tmp_metabolite.name[:-len(tmp_formula) - 1] tmp_metabolite.formula = tmp_formula metabolite_dict.update({metabolite_id: tmp_metabolite}) metabolites.append(tmp_metabolite) cobra_model.add_metabolites(metabolites) # Construct the vectors and matrices for holding connectivity and numerical # info to feed to the cobra toolbox. # Always assume steady state simulations so b is set to 0 cobra_reaction_list = [] coefficients = {} for sbml_reaction in sbml_reactions: if use_hyphens: # Change the ids to match conventions used by the Palsson lab. reaction = Reaction(reaction_re.split( sbml_reaction.getId())[-1].replace('__', '-')) else: # Just in case the SBML ids are ill-formed and use - reaction = Reaction(reaction_re.split( sbml_reaction.getId())[-1].replace('-', '__')) cobra_reaction_list.append(reaction) # reaction.exchange_reaction = 0 reaction.name = sbml_reaction.getName() cobra_metabolites = {} # Use the cobra.Metabolite class here for sbml_metabolite in sbml_reaction.getListOfReactants(): tmp_metabolite_id = sbml_metabolite.getSpecies() # This deals with boundary metabolites if tmp_metabolite_id in metabolite_dict: tmp_metabolite = metabolite_dict[tmp_metabolite_id] cobra_metabolites[tmp_metabolite] = - \ sbml_metabolite.getStoichiometry() for sbml_metabolite in sbml_reaction.getListOfProducts(): tmp_metabolite_id = sbml_metabolite.getSpecies() # This deals with boundary metabolites if tmp_metabolite_id in metabolite_dict: tmp_metabolite = metabolite_dict[tmp_metabolite_id] # Handle the case where the metabolite was specified both # as a reactant and as a product. if tmp_metabolite in cobra_metabolites: warn("%s appears as a reactant and product %s" % (tmp_metabolite_id, reaction.id)) cobra_metabolites[ tmp_metabolite] += sbml_metabolite.getStoichiometry() # if the combined stoichiometry is 0, remove the metabolite if cobra_metabolites[tmp_metabolite] == 0: cobra_metabolites.pop(tmp_metabolite) else: cobra_metabolites[ tmp_metabolite] = sbml_metabolite.getStoichiometry() # check for nan for met, v in iteritems(cobra_metabolites): if isnan(v) or isinf(v): warn("invalid value %s for metabolite '%s' in reaction '%s'" % (str(v), met.id, reaction.id)) reaction.add_metabolites(cobra_metabolites) # Parse the kinetic law info here. parameter_dict = {} # If lower and upper bounds are specified in the Kinetic Law then # they override the sbml reversible attribute. If they are not # specified then the bounds are determined by getReversible. if not sbml_reaction.getKineticLaw(): if sbml_reaction.getReversible(): parameter_dict['lower_bound'] = __default_lower_bound parameter_dict['upper_bound'] = __default_upper_bound else: # Assume that irreversible reactions only proceed from left to # right. parameter_dict['lower_bound'] = 0 parameter_dict['upper_bound'] = __default_upper_bound parameter_dict[ 'objective_coefficient'] = __default_objective_coefficient else: for sbml_parameter in \ sbml_reaction.getKineticLaw().getListOfParameters(): parameter_dict[ sbml_parameter.getId().lower()] = sbml_parameter.getValue() if 'lower_bound' in parameter_dict: reaction.lower_bound = parameter_dict['lower_bound'] elif 'lower bound' in parameter_dict: reaction.lower_bound = parameter_dict['lower bound'] elif sbml_reaction.getReversible(): reaction.lower_bound = __default_lower_bound else: reaction.lower_bound = 0 if 'upper_bound' in parameter_dict: reaction.upper_bound = parameter_dict['upper_bound'] elif 'upper bound' in parameter_dict: reaction.upper_bound = parameter_dict['upper bound'] else: reaction.upper_bound = __default_upper_bound objective_coefficient = parameter_dict.get( 'objective_coefficient', parameter_dict.get( 'objective_coefficient', __default_objective_coefficient)) if objective_coefficient != 0: coefficients[reaction] = objective_coefficient # ensure values are not set to nan or inf if isnan(reaction.lower_bound) or isinf(reaction.lower_bound): reaction.lower_bound = __default_lower_bound if isnan(reaction.upper_bound) or isinf(reaction.upper_bound): reaction.upper_bound = __default_upper_bound reaction_note_dict = parse_legacy_sbml_notes( sbml_reaction.getNotesString()) # Parse the reaction notes. # POTENTIAL BUG: DEALING WITH LEGACY 'SBML' THAT IS NOT IN A # STANDARD FORMAT # TODO: READ IN OTHER NOTES AND GIVE THEM A reaction_ prefix. # TODO: Make sure genes get added as objects if 'GENE ASSOCIATION' in reaction_note_dict: rule = reaction_note_dict['GENE ASSOCIATION'][0] try: rule.encode('ascii') except (UnicodeEncodeError, UnicodeDecodeError): warn("gene_reaction_rule '%s' is not ascii compliant" % rule) if rule.startswith(""") and rule.endswith("""): rule = rule[6:-6] reaction.gene_reaction_rule = rule if 'GENE LIST' in reaction_note_dict: reaction.systematic_names = reaction_note_dict['GENE LIST'][0] elif ('GENES' in reaction_note_dict and reaction_note_dict['GENES'] != ['']): reaction.systematic_names = reaction_note_dict['GENES'][0] elif 'LOCUS' in reaction_note_dict: gene_id_to_object = dict([(x.id, x) for x in reaction._genes]) for the_row in reaction_note_dict['LOCUS']: tmp_row_dict = {} the_row = 'LOCUS:' + the_row.lstrip('_').rstrip('#') for the_item in the_row.split('#'): k, v = the_item.split(':') tmp_row_dict[k] = v tmp_locus_id = tmp_row_dict['LOCUS'] if 'TRANSCRIPT' in tmp_row_dict: tmp_locus_id = tmp_locus_id + \ '.' + tmp_row_dict['TRANSCRIPT'] if 'ABBREVIATION' in tmp_row_dict: gene_id_to_object[tmp_locus_id].name = tmp_row_dict[ 'ABBREVIATION'] if 'SUBSYSTEM' in reaction_note_dict: reaction.subsystem = reaction_note_dict.pop('SUBSYSTEM')[0] reaction.notes = reaction_note_dict # Now, add all of the reactions to the model. cobra_model.id = sbml_model.getId() # Populate the compartment list - This will be done based on # cobra.Metabolites in cobra.Reactions in the future. cobra_model.compartments = compartment_dict cobra_model.add_reactions(cobra_reaction_list) set_objective(cobra_model, coefficients) return cobra_model
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)
def parse_xml_into_model(xml, number=float): xml_model = xml.find(ns("sbml:model")) if get_attrib(xml_model, "fbc:strict") != "true": warn('loading SBML model without fbc:strict="true"') model_id = get_attrib(xml_model, "id") model = Model(model_id) model.name = xml_model.get("name") model.compartments = {c.get("id"): c.get("name") for c in xml_model.findall(COMPARTMENT_XPATH)} # add metabolites for species in xml_model.findall(SPECIES_XPATH % 'false'): met = get_attrib(species, "id", require=True) met = Metabolite(clip(met, "M_")) met.name = species.get("name") annotate_cobra_from_sbml(met, species) met.compartment = species.get("compartment") met.charge = get_attrib(species, "fbc:charge", int) met.formula = get_attrib(species, "fbc:chemicalFormula") model.add_metabolites([met]) # Detect boundary metabolites - In case they have been mistakenly # added. They should not actually appear in a model boundary_metabolites = {clip(i.get("id"), "M_") for i in xml_model.findall(SPECIES_XPATH % 'true')} # add genes for sbml_gene in xml_model.iterfind(GENES_XPATH): gene_id = get_attrib(sbml_gene, "fbc:id").replace(SBML_DOT, ".") gene = Gene(clip(gene_id, "G_")) gene.name = get_attrib(sbml_gene, "fbc:name") if gene.name is None: gene.name = get_attrib(sbml_gene, "fbc:label") annotate_cobra_from_sbml(gene, sbml_gene) model.genes.append(gene) def process_gpr(sub_xml): """recursively convert gpr xml to a gpr string""" if sub_xml.tag == OR_TAG: return "( " + ' or '.join(process_gpr(i) for i in sub_xml) + " )" elif sub_xml.tag == AND_TAG: return "( " + ' and '.join(process_gpr(i) for i in sub_xml) + " )" elif sub_xml.tag == GENEREF_TAG: gene_id = get_attrib(sub_xml, "fbc:geneProduct", require=True) return clip(gene_id, "G_") else: raise Exception("unsupported tag " + sub_xml.tag) bounds = {bound.get("id"): get_attrib(bound, "value", type=number) for bound in xml_model.iterfind(BOUND_XPATH)} # add reactions reactions = [] for sbml_reaction in xml_model.iterfind( ns("sbml:listOfReactions/sbml:reaction")): reaction = get_attrib(sbml_reaction, "id", require=True) reaction = Reaction(clip(reaction, "R_")) reaction.name = sbml_reaction.get("name") annotate_cobra_from_sbml(reaction, sbml_reaction) lb_id = get_attrib(sbml_reaction, "fbc:lowerFluxBound", require=True) ub_id = get_attrib(sbml_reaction, "fbc:upperFluxBound", require=True) try: reaction.upper_bound = bounds[ub_id] reaction.lower_bound = bounds[lb_id] except KeyError as e: raise CobraSBMLError("No constant bound with id '%s'" % str(e)) reactions.append(reaction) stoichiometry = defaultdict(lambda: 0) for species_reference in sbml_reaction.findall( ns("sbml:listOfReactants/sbml:speciesReference")): met_name = clip(species_reference.get("species"), "M_") stoichiometry[met_name] -= \ number(species_reference.get("stoichiometry")) for species_reference in sbml_reaction.findall( ns("sbml:listOfProducts/sbml:speciesReference")): met_name = clip(species_reference.get("species"), "M_") stoichiometry[met_name] += \ get_attrib(species_reference, "stoichiometry", type=number, require=True) # needs to have keys of metabolite objects, not ids object_stoichiometry = {} for met_id in stoichiometry: if met_id in boundary_metabolites: warn("Boundary metabolite '%s' used in reaction '%s'" % (met_id, reaction.id)) continue try: metabolite = model.metabolites.get_by_id(met_id) except KeyError: warn("ignoring unknown metabolite '%s' in reaction %s" % (met_id, reaction.id)) continue object_stoichiometry[metabolite] = stoichiometry[met_id] reaction.add_metabolites(object_stoichiometry) # set gene reaction rule gpr_xml = sbml_reaction.find(GPR_TAG) if gpr_xml is not None and len(gpr_xml) != 1: warn("ignoring invalid geneAssociation for " + repr(reaction)) gpr_xml = None gpr = process_gpr(gpr_xml[0]) if gpr_xml is not None else '' # remove outside parenthesis, if any if gpr.startswith("(") and gpr.endswith(")"): gpr = gpr[1:-1].strip() gpr = gpr.replace(SBML_DOT, ".") reaction.gene_reaction_rule = gpr try: model.add_reactions(reactions) except ValueError as e: warn(str(e)) # objective coefficients are handled after all reactions are added obj_list = xml_model.find(ns("fbc:listOfObjectives")) if obj_list is None: warn("listOfObjectives element not found") return model target_objective_id = get_attrib(obj_list, "fbc:activeObjective") target_objective = obj_list.find( ns("fbc:objective[@fbc:id='{}']".format(target_objective_id))) obj_direction_long = get_attrib(target_objective, "fbc:type") obj_direction = LONG_SHORT_DIRECTION[obj_direction_long] obj_query = OBJECTIVES_XPATH % target_objective_id coefficients = {} for sbml_objective in obj_list.findall(obj_query): rxn_id = clip(get_attrib(sbml_objective, "fbc:reaction"), "R_") try: objective_reaction = model.reactions.get_by_id(rxn_id) except KeyError: raise CobraSBMLError("Objective reaction '%s' not found" % rxn_id) try: coefficients[objective_reaction] = get_attrib( sbml_objective, "fbc:coefficient", type=number) except ValueError as e: warn(str(e)) set_objective(model, coefficients) model.solver.objective.direction = obj_direction return model
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