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_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 test_gpr(): model = Model() reaction = Reaction("test") # Set GPR to a reaction not in a model reaction.gene_reaction_rule = "(g1 or g2) and g3" assert reaction.gene_reaction_rule == "(g1 or g2) and g3" assert len(reaction.genes) == 3 # Adding reaction with a GPR propagates to the model model.add_reactions([reaction]) assert len(model.genes) == 3 # Ensure the gene objects are the same in the model and reaction reaction_gene = list(reaction.genes)[0] model_gene = model.genes.get_by_id(reaction_gene.id) assert reaction_gene is model_gene # Test ability to handle uppercase AND/OR with warnings.catch_warnings(): warnings.simplefilter("ignore") reaction.gene_reaction_rule = "(b1 AND b2) OR (b3 and b4)" assert reaction.gene_reaction_rule == "(b1 and b2) or (b3 and b4)" assert len(reaction.genes) == 4 # Ensure regular expressions correctly extract genes from malformed # GPR string with warnings.catch_warnings(): warnings.simplefilter("ignore") reaction.gene_reaction_rule = "(a1 or a2" assert len(reaction.genes) == 2 reaction.gene_reaction_rule = "(forT or " assert len(reaction.genes) == 1
def test_reverse_reaction(): model = Model() reaction = Reaction('AB') model.add_reaction(reaction) reaction.build_reaction_from_string('a --> b') _reverse_reaction(reaction) assert reaction.reaction == 'b <-- a'
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_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 test_gpr(self): model = Model() reaction = Reaction("test") # set a gpr to reaction not in a model reaction.gene_reaction_rule = "(g1 or g2) and g3" assert reaction.gene_reaction_rule == "(g1 or g2) and g3" assert len(reaction.genes) == 3 # adding reaction with a GPR propagates to the model model.add_reaction(reaction) assert len(model.genes) == 3 # ensure the gene objects are the same in the model and reaction reaction_gene = list(reaction.genes)[0] model_gene = model.genes.get_by_id(reaction_gene.id) assert reaction_gene is model_gene # test ability to handle uppercase AND/OR with warnings.catch_warnings(): warnings.simplefilter("ignore") reaction.gene_reaction_rule = "(b1 AND b2) OR (b3 and b4)" assert reaction.gene_reaction_rule == "(b1 and b2) or (b3 and b4)" assert len(reaction.genes) == 4 # ensure regular expressions correctly extract genes from malformed # GPR string with warnings.catch_warnings(): warnings.simplefilter("ignore") reaction.gene_reaction_rule = "(a1 or a2" assert len(reaction.genes) == 2 reaction.gene_reaction_rule = "(forT or " assert len(reaction.genes) == 1
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_loopless_solution(ll_test_model: Model) -> None: """Test loopless_solution().""" solution_feasible = loopless_solution(ll_test_model) ll_test_model.reactions.v3.lower_bound = 1 ll_test_model.optimize() solution_infeasible = loopless_solution(ll_test_model) assert solution_feasible.fluxes["v3"] == 0.0 assert solution_infeasible.fluxes["v3"] == 1.0
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_find_boundary_types_demand(model: Model) -> None: """Test boundary type identification for demands.""" dm = Reaction("demand") model.add_reaction(dm) dm.build_reaction_from_string("atp_c ->") dm = model.demands assert len(dm) == 1 assert "demand" in [r.id for r in dm]
def test_transfer_objective(self, model): new_mod = Model("new model") new_mod.add_reactions(model.reactions) new_mod.objective = model.objective assert (set(str(x) for x in model.objective.expression.args) == set( str(x) for x in new_mod.objective.expression.args)) new_mod.solver.optimize() assert abs(new_mod.objective.value - 0.874) < 0.001
def test_prune_unused_reactions_output_type(model: Model) -> None: """Test the output type of unused reactions pruning.""" reaction = Reaction("foo") model.add_reactions([reaction]) model_pruned, unused = prune_unused_reactions(model) assert isinstance(model_pruned, Model) # test that the output contains reaction objects assert isinstance(unused[0], Reaction)
def test_add_loopless(ll_test_model: Model) -> None: """Test add_loopless().""" add_loopless(ll_test_model) feasible_status = ll_test_model.optimize().status ll_test_model.reactions.v3.lower_bound = 1 ll_test_model.slim_optimize() infeasible_status = ll_test_model.solver.status assert feasible_status == OPTIMAL assert infeasible_status == INFEASIBLE
def tiny_toy_model(): model = Model("Toy Model") m1 = Metabolite("M1") d1 = Reaction("ex1") d1.add_metabolites({m1: -1}) d1.upper_bound = 0 d1.lower_bound = -1000 model.add_reactions([d1]) return model
def test_find_boundary_types_sink(model: Model) -> None: """Test boundary type identification for sinks.""" sn = Reaction("sink") model.add_reaction(sn) sn.build_reaction_from_string("atp_c <->") sn.bounds = -1000, 1000 sn = model.sinks assert len(sn) == 1 assert "sink" in [r.id for r in sn]
def test_gene_knockout(salmonella: Model) -> None: """Test gene knockout.""" gene_list = ["STM1067", "STM0227"] dependent_reactions = { "3HAD121", "3HAD160", "3HAD80", "3HAD140", "3HAD180", "3HAD100", "3HAD181", "3HAD120", "3HAD60", "3HAD141", "3HAD161", "T2DECAI", "3HAD40", } _gene_knockout_computation(salmonella, gene_list, dependent_reactions) _gene_knockout_computation(salmonella, ["STM4221"], {"PGI"}) _gene_knockout_computation(salmonella, ["STM1746.S"], {"4PEPTabcpp"}) # test cumulative behavior delete_model_genes(salmonella, gene_list[:1]) delete_model_genes(salmonella, gene_list[1:], cumulative_deletions=True) delete_model_genes(salmonella, ["STM4221"], cumulative_deletions=True) dependent_reactions.add("PGI") assert _get_removed(salmonella) == dependent_reactions # non-cumulative following cumulative delete_model_genes(salmonella, ["STM4221"], cumulative_deletions=False) assert _get_removed(salmonella) == {"PGI"} # make sure on reset that the bounds are correct reset_bound = salmonella.reactions.get_by_id("T2DECAI").upper_bound assert reset_bound == 1000.0 # test computation when gene name is a subset of another test_model = Model() test_reaction_1 = Reaction("test1") test_reaction_1.gene_reaction_rule = "eggs or (spam and eggspam)" test_model.add_reactions([test_reaction_1]) _gene_knockout_computation(test_model, ["eggs"], set()) _gene_knockout_computation(test_model, ["eggs", "spam"], {"test1"}) # test computation with nested boolean expression test_reaction_1.gene_reaction_rule = "g1 and g2 and (g3 or g4 or (g5 and g6))" _gene_knockout_computation(test_model, ["g3"], set()) _gene_knockout_computation(test_model, ["g1"], {"test1"}) _gene_knockout_computation(test_model, ["g5"], set()) _gene_knockout_computation(test_model, ["g3", "g4", "g5"], {"test1"}) # test computation when gene names are python expressions test_reaction_1.gene_reaction_rule = "g1 and (for or in)" _gene_knockout_computation(test_model, ["for", "in"], {"test1"}) _gene_knockout_computation(test_model, ["for"], set()) test_reaction_1.gene_reaction_rule = "g1 and g2 and g2.conjugate" _gene_knockout_computation(test_model, ["g2"], {"test1"}) _gene_knockout_computation(test_model, ["g2.conjugate"], {"test1"}) test_reaction_1.gene_reaction_rule = "g1 and (try:' or 'except:1)" _gene_knockout_computation(test_model, ["try:'"], set()) _gene_knockout_computation(test_model, ["try:'", "'except:1"], {"test1"})
def test_inequality_constraint(model: Model) -> None: """Test inequality constraint.""" co = model.problem.Constraint(model.reactions.ACALD.flux_expression, lb=-0.5) model.add_cons_vars(co) s = sample(model, 10) assert all(s.ACALD > -0.5 - 1e-6) s = sample(model, 10, method="achr") assert all(s.ACALD > -0.5 - 1e-6)
def test_compartments(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_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_single_point_space(model: Model) -> None: """Test the reduction of the sampling space to one point.""" pfba_sol = pfba(model) pfba_const = model.problem.Constraint( sum(model.variables), ub=pfba_sol.objective_value ) model.add_cons_vars(pfba_const) model.reactions.Biomass_Ecoli_core.lower_bound = pfba_sol.fluxes.Biomass_Ecoli_core with pytest.raises(ValueError): sample(model, 1)
def test_find_external_compartment_multi(model: Model) -> None: """Test multiple external compartment identification.""" for r in model.reactions: r._compartments = None model.exchanges[0].reactants[0].compartment = "extracellular" # still works due to different boundary numbers assert find_external_compartment(model) == "e" model.exchanges[1].reactants[0].compartment = "extra cellular" model.remove_reactions(model.exchanges) # Now fails because same boundary count with pytest.raises(RuntimeError): find_external_compartment(model)
def test_model_less_reaction(model: Model) -> None: """Test model without reactions.""" model.slim_optimize() for reaction in model.reactions: assert isinstance(reaction.flux, float) assert isinstance(reaction.reduced_cost, float) for reaction in model.reactions: model.remove_reactions([reaction]) with pytest.raises(RuntimeError): reaction.flux with pytest.raises(RuntimeError): reaction.reduced_cost
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 solver_test(request): solver = solvers.solver_dict[request.param] old_solution = 0.8739215 infeasible_model = Model() metabolite_1 = Metabolite("met1") reaction_1 = Reaction("rxn1") reaction_2 = Reaction("rxn2") reaction_1.add_metabolites({metabolite_1: 1}) reaction_2.add_metabolites({metabolite_1: 1}) reaction_1.lower_bound = 1 reaction_2.upper_bound = 2 infeasible_model.add_reactions([reaction_1, reaction_2]) return solver, old_solution, infeasible_model
def test_prune_unused_rxns_functionality(model: Model) -> None: """Test the sanity of unused reactions pruning.""" for x in ["foo1", "foo2", "foo3"]: model.add_reactions([Reaction(x)]) model_pruned, unused = prune_unused_reactions(model) assert "foo1" in model.reactions assert "foo2" in model.reactions assert "foo3" in model.reactions # test that the unused reactions are not used in the model assert "foo1" not in model_pruned.reactions assert "foo2" not in model_pruned.reactions assert "foo3" not in model_pruned.reactions
def test_custom_hashes(): # These hashes are generated from old IDs in models (in reaction strings), # and they match to these corrected BiGG reaction IDs cases = [ ('39b5f90a1919aef07473e2f835ce63af', 'EX_frmd_e', 'foam_e <=>'), ('92f1047c72db0a36413d822863be514e', 'EX_phllqne_e', 'phyQ_e <=>'), ] model = Model() for reaction_hash, bigg_id, reaction_string in cases: reaction = Reaction(bigg_id) model.add_reaction(reaction) reaction.build_reaction_from_string(reaction_string) lookup_dict = {m.id: m.id for m in model.metabolites} assert hash_reaction(reaction, lookup_dict) == reaction_hash
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_sbo_annotation(model: Model) -> None: """Test SBO annotation function.""" rxns = model.reactions rxns.EX_o2_e.annotation.clear() fake_DM = Reaction("DM_h_c") model.add_reactions([fake_DM]) fake_DM.add_metabolites({model.metabolites.get_by_id("h_c"): -1}) # this exchange will be set wrong. The function should not overwrite # an existing SBO annotation rxns.get_by_id("EX_h_e").annotation["sbo"] = "SBO:0000628" add_SBO(model) assert rxns.EX_o2_e.annotation["sbo"] == "SBO:0000627" assert rxns.DM_h_c.annotation["sbo"] == "SBO:0000628" assert rxns.EX_h_e.annotation["sbo"] == "SBO:0000628"
def test_add_metabolite(model: Model) -> None: """Test metabolite addition to a reaction from an unsolved 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 __json_decode__(self, **attrs): """build a model from a dict""" Model.__init__(self) if 'reactions' not in attrs: raise Exception('JSON object has no reactions attribute. Cannot load.') self.add_metabolites( [cobra.io.dict.metabolite_from_dict(metabolite) for metabolite in attrs['metabolites']] ) self.genes = DictList(attrs['genes']) self.add_reactions( [cobra.io.dict.reaction_from_dict(reaction, self) for reaction in attrs['reactions']] ) for k, v in attrs.items(): if k in {'id', 'name', 'notes', 'compartments', 'annotation'}: setattr(self, k, v)
def __init__( self, model, universal=None, lower_bound=0.05, penalties=None, exchange_reactions=False, demand_reactions=True, integer_threshold=1e-6, ): self.original_model = model self.lower_bound = lower_bound self.model = model.copy() tolerances = self.model.solver.configuration.tolerances try: tolerances.integrality = integer_threshold except AttributeError: logger.warning( f"The current solver interface {interface_to_str(self.model.problem)} " f"doesn't support setting the integrality tolerance." ) # TODO (Midnighter): One could debate how useful it is to compare against this # threshold when it is not supported by the chosen solver. self.integer_threshold = integer_threshold self.universal = universal.copy() if universal else Model("universal") self.penalties = dict(universal=1, exchange=100, demand=1) if penalties is not None: self.penalties.update(penalties) self.indicators = list() self.costs = dict() self.extend_model(exchange_reactions, demand_reactions) fix_objective_as_constraint(self.model, bound=lower_bound) self.add_switches_and_objective()
def test_toy_model_tolerance_with_different_default(): """Verify that different default tolerance is respected by Model.""" config = Configuration() config.tolerance = 1e-05 toy_model = Model(name="toy model") assert toy_model.tolerance == 1e-05
def __init__(self, list_of_models=[], identifier=None, name=None): Object.__init__(self, identifier, name) if len(list_of_models) > 1: if not all(isinstance(x, Model) for x in list_of_models): raise AttributeError( "list_of_models may only contain cobra.core.Model objects") if len([model.id for model in list_of_models]) > \ len(set([model.id for model in list_of_models])): raise AssertionError( "Ensemble members cannot have duplicate model ids.") self.features = DictList() self._populate_features_base(list_of_models) self.members = DictList() self._populate_members(list_of_models) else: if len(list_of_models) == 0: self.base_model = Model(id_or_model=identifier+'_base_model',\ name=name) else: if not isinstance(list_of_models[0], Model): raise AttributeError( "list_of_models may only contain cobra.core.Model objects" ) self.base_model = list_of_models[0]
def __init__(self, model, universal=None, lower_bound=0.05, penalties=None, exchange_reactions=False, demand_reactions=True, integer_threshold=1e-6): self.original_model = model self.lower_bound = lower_bound self.model = model.copy() # TODO: adjust once optlang supports integrality constraint settings try: self.model.solver.configuration._iocp.tol_int = integer_threshold except AttributeError: try: self.model.solver.problem.parameters.mip.tolerances. \ integrality.set(integer_threshold) except AttributeError: warn("tried to set integrality constraint, but don't know " "how to do that for " "solver {}".format(self.model.problem.__name__)) self.universal = universal.copy() if universal else Model('universal') self.penalties = dict(universal=1, exchange=100, demand=1) if penalties is not None: self.penalties.update(penalties) self.integer_threshold = integer_threshold self.indicators = list() self.costs = dict() self.extend_model(exchange_reactions, demand_reactions) fix_objective_as_constraint(self.model, bound=lower_bound) self.add_switches_and_objective()
def model_from_dict(obj): """Build a model from a dict. Models stored in json are first formulated as a dict that can be read to cobra model using this function. Parameters ---------- obj : dict A dictionary with elements, 'genes', 'compartments', 'id', 'metabolites', 'notes' and 'reactions'; where 'metabolites', 'genes' and 'metabolites' are in turn lists with dictionaries holding all attributes to form the corresponding object. Returns ------- cora.core.Model The generated model. See Also -------- cobra.io.model_to_dict """ if 'reactions' not in obj: raise ValueError('Object has no reactions attribute. Cannot load.') model = Model() model.add_metabolites( [metabolite_from_dict(metabolite) for metabolite in obj['metabolites']] ) model.genes.extend([gene_from_dict(gene) for gene in obj['genes']]) model.add_reactions( [reaction_from_dict(reaction, model) for reaction in obj['reactions']] ) objective_reactions = [rxn for rxn in obj['reactions'] if rxn.get('objective_coefficient', 0) != 0] coefficients = { model.reactions.get_by_id(rxn['id']): rxn['objective_coefficient'] for rxn in objective_reactions} set_objective(model, coefficients) for k, v in iteritems(obj): if k in {'id', 'name', 'notes', 'compartments', 'annotation'}: setattr(model, k, v) return model
def test_transfer_objective(self, model): new_mod = Model("new model") new_mod.add_reactions(model.reactions) new_mod.objective = model.objective assert (set(str(x) for x in model.objective.expression.args) == set( str(x) for x in new_mod.objective.expression.args)) new_mod.slim_optimize() assert abs(new_mod.objective.value - 0.874) < 0.001
def test_remove_genes(self): m = Model("test") m.add_reactions([Reaction("r" + str(i + 1)) for i in range(8)]) self.assertEqual(len(m.reactions), 8) rxns = m.reactions rxns.r1.gene_reaction_rule = "(a and b) or (c and a)" rxns.r2.gene_reaction_rule = "(a and b and d and e)" rxns.r3.gene_reaction_rule = "(a and b) or (b and c)" rxns.r4.gene_reaction_rule = "(f and b) or (b and c)" rxns.r5.gene_reaction_rule = "x" rxns.r6.gene_reaction_rule = "y" rxns.r7.gene_reaction_rule = "x or z" rxns.r8.gene_reaction_rule = "" self.assertIn("a", m.genes) self.assertIn("x", m.genes) remove_genes(m, ["a"], remove_reactions=False) self.assertNotIn("a", m.genes) self.assertIn("x", m.genes) self.assertEqual(rxns.r1.gene_reaction_rule, "") self.assertEqual(rxns.r2.gene_reaction_rule, "") self.assertEqual(rxns.r3.gene_reaction_rule, "b and c") self.assertEqual(rxns.r4.gene_reaction_rule, "(f and b) or (b and c)") self.assertEqual(rxns.r5.gene_reaction_rule, "x") self.assertEqual(rxns.r6.gene_reaction_rule, "y") self.assertEqual(rxns.r7.genes, {m.genes.x, m.genes.z}) self.assertEqual(rxns.r8.gene_reaction_rule, "") remove_genes(m, ["x"], remove_reactions=True) self.assertEqual(len(m.reactions), 7) self.assertNotIn("r5", m.reactions) self.assertNotIn("x", m.genes) self.assertEqual(rxns.r1.gene_reaction_rule, "") self.assertEqual(rxns.r2.gene_reaction_rule, "") self.assertEqual(rxns.r3.gene_reaction_rule, "b and c") self.assertEqual(rxns.r4.gene_reaction_rule, "(f and b) or (b and c)") self.assertEqual(rxns.r6.gene_reaction_rule, "y") self.assertEqual(rxns.r7.gene_reaction_rule, "z") self.assertEqual(rxns.r7.genes, {m.genes.z}) self.assertEqual(rxns.r8.gene_reaction_rule, "")
def room_model(): """ Generate ROOM model as described in [1]_ References ---------- .. [1] Tomer Shlomi, Omer Berkman and Eytan Ruppin, "Regulatory on/off minimization of metabolic flux changes after genetic perturbations", PNAS 2005 102 (21) 7695-7700; doi:10.1073/pnas.0406346102 """ test_model = Model("papin_2003") v_1 = Reaction("v1") v_2 = Reaction("v2") v_3 = Reaction("v3") v_4 = Reaction("v4") v_5 = Reaction("v5") v_6 = Reaction("v6", upper_bound=0.0) b_1 = Reaction("b1", upper_bound=10.0, lower_bound=0.0) b_2 = Reaction("b2") b_3 = Reaction("b3") test_model.add_reactions([v_1, v_2, v_3, v_4, v_5, v_6, b_1, b_2, b_3]) v_1.reaction = "A -> B" v_2.reaction = "2 B -> C + byp" v_3.reaction = "2 B + cof -> D" v_4.reaction = "D -> E + cof" v_5.reaction = "C + cof -> D" v_6.reaction = "C -> E" b_1.reaction = "-> A" b_2.reaction = "E ->" b_3.reaction = "byp ->" test_model.objective = 'b2' return test_model
def test_remove_genes(self): m = Model("test") m.add_reactions([Reaction("r" + str(i + 1)) for i in range(8)]) assert len(m.reactions) == 8 rxns = m.reactions rxns.r1.gene_reaction_rule = "(a and b) or (c and a)" rxns.r2.gene_reaction_rule = "(a and b and d and e)" rxns.r3.gene_reaction_rule = "(a and b) or (b and c)" rxns.r4.gene_reaction_rule = "(f and b) or (b and c)" rxns.r5.gene_reaction_rule = "x" rxns.r6.gene_reaction_rule = "y" rxns.r7.gene_reaction_rule = "x or z" rxns.r8.gene_reaction_rule = "" assert "a" in m.genes assert "x" in m.genes remove_genes(m, ["a"], remove_reactions=False) assert "a" not in m.genes assert "x" in m.genes assert rxns.r1.gene_reaction_rule == "" assert rxns.r2.gene_reaction_rule == "" assert rxns.r3.gene_reaction_rule == "b and c" assert rxns.r4.gene_reaction_rule == "(f and b) or (b and c)" assert rxns.r5.gene_reaction_rule == "x" assert rxns.r6.gene_reaction_rule == "y" assert rxns.r7.genes == {m.genes.x, m.genes.z} assert rxns.r8.gene_reaction_rule == "" remove_genes(m, ["x"], remove_reactions=True) assert len(m.reactions) == 7 assert "r5" not in m.reactions assert "x" not in m.genes assert rxns.r1.gene_reaction_rule == "" assert rxns.r2.gene_reaction_rule == "" assert rxns.r3.gene_reaction_rule == "b and c" assert rxns.r4.gene_reaction_rule == "(f and b) or (b and c)" assert rxns.r6.gene_reaction_rule == "y" assert rxns.r7.gene_reaction_rule == "z" assert rxns.r7.genes == {m.genes.z} assert rxns.r8.gene_reaction_rule == ""
def construct_papin_2003_model(): test_model = Model("papin_2003") v1 = Reaction("v1") v2 = Reaction("v2") v3 = Reaction("v3") v4 = Reaction("v4") v5 = Reaction("v5") v6 = Reaction("v6", upper_bound=0.0) b1 = Reaction("b1", upper_bound=10.0, lower_bound=0.0) b2 = Reaction("b2") b3 = Reaction("b3") test_model.add_reactions([v1, v2, v3, v4, v5, v6, b1, b2, b3]) v1.reaction = "A -> B" v2.reaction = "2 B -> C + byp" v3.reaction = "2 B + cof -> D" v4.reaction = "D -> E + cof" v5.reaction = "C + cof -> D" v6.reaction = "C -> E" b1.reaction = "-> A" b2.reaction = "E ->" b3.reaction = "byp ->" test_model.objective = 'b2' return test_model
def create_consisten_model(model,metamodel,consistent_reactions): consistent_model = Model() consistent_model.id = model.id consistent_model.description = model.id auxiliar_gene = Gene('MODULAR_GAPFILLING') auxiliar_gene._model = consistent_model consistent_model.genes.append(auxiliar_gene) for reaction_id in consistent_reactions: new_reaction = metamodel.reactions.get_by_id(reaction_id).copy() if reaction_id in model.reactions: reaction_reference = model.reactions.get_by_id(reaction_id) gene_list = [] for gene in reaction_reference.genes: if gene.id in consistent_model.genes: gene_list.append(consistent_model.genes.get_by_id(gene.id)) else: new_gene = Gene(gene.id) new_gene._model = consistent_model consistent_model.genes.append(new_gene) gene_list.append(new_gene) for gene in gene_list: gene._reaction.add(new_reaction) new_reaction._genes = gene_list new_reaction.gene_reaction_rule = reaction_reference.gene_reaction_rule else: new_reaction.gene_reaction_rule = auxiliar_gene.name auxiliar_gene._reaction.add(new_reaction) consistent_model.add_reaction(new_reaction) return consistent_model
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 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 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 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 test_gene_knockout_computation(self, salmonella): def find_gene_knockout_reactions_fast(cobra_model, gene_list): compiled_rules = get_compiled_gene_reaction_rules( cobra_model) return find_gene_knockout_reactions( cobra_model, gene_list, compiled_gene_reaction_rules=compiled_rules) def get_removed(m): return {x.id for x in m._trimmed_reactions} def test_computation(m, gene_ids, expected_reaction_ids): genes = [m.genes.get_by_id(i) for i in gene_ids] expected_reactions = {m.reactions.get_by_id(i) for i in expected_reaction_ids} removed1 = set(find_gene_knockout_reactions(m, genes)) removed2 = set(find_gene_knockout_reactions_fast(m, genes)) assert removed1 == expected_reactions assert removed2 == expected_reactions delete_model_genes(m, gene_ids, cumulative_deletions=False) assert get_removed(m) == expected_reaction_ids undelete_model_genes(m) gene_list = ['STM1067', 'STM0227'] dependent_reactions = {'3HAD121', '3HAD160', '3HAD80', '3HAD140', '3HAD180', '3HAD100', '3HAD181', '3HAD120', '3HAD60', '3HAD141', '3HAD161', 'T2DECAI', '3HAD40'} test_computation(salmonella, gene_list, dependent_reactions) test_computation(salmonella, ['STM4221'], {'PGI'}) test_computation(salmonella, ['STM1746.S'], {'4PEPTabcpp'}) # test cumulative behavior delete_model_genes(salmonella, gene_list[:1]) delete_model_genes(salmonella, gene_list[1:], cumulative_deletions=True) delete_model_genes(salmonella, ["STM4221"], cumulative_deletions=True) dependent_reactions.add('PGI') assert get_removed(salmonella) == dependent_reactions # non-cumulative following cumulative delete_model_genes(salmonella, ["STM4221"], cumulative_deletions=False) assert get_removed(salmonella) == {'PGI'} # make sure on reset that the bounds are correct reset_bound = salmonella.reactions.get_by_id("T2DECAI").upper_bound assert reset_bound == 1000. # test computation when gene name is a subset of another test_model = Model() test_reaction_1 = Reaction("test1") test_reaction_1.gene_reaction_rule = "eggs or (spam and eggspam)" test_model.add_reaction(test_reaction_1) test_computation(test_model, ["eggs"], set()) test_computation(test_model, ["eggs", "spam"], {'test1'}) # test computation with nested boolean expression test_reaction_1.gene_reaction_rule = \ "g1 and g2 and (g3 or g4 or (g5 and g6))" test_computation(test_model, ["g3"], set()) test_computation(test_model, ["g1"], {'test1'}) test_computation(test_model, ["g5"], set()) test_computation(test_model, ["g3", "g4", "g5"], {'test1'}) # test computation when gene names are python expressions test_reaction_1.gene_reaction_rule = "g1 and (for or in)" test_computation(test_model, ["for", "in"], {'test1'}) test_computation(test_model, ["for"], set()) test_reaction_1.gene_reaction_rule = "g1 and g2 and g2.conjugate" test_computation(test_model, ["g2"], {"test1"}) test_computation(test_model, ["g2.conjugate"], {"test1"}) test_reaction_1.gene_reaction_rule = "g1 and (try:' or 'except:1)" test_computation(test_model, ["try:'"], set()) test_computation(test_model, ["try:'", "'except:1"], {"test1"})
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 test_gapfilling(self, salmonella): m = Model() m.add_metabolites([Metabolite(m_id) for m_id in ["a", "b", "c"]]) exa = Reaction("EX_a") exa.add_metabolites({m.metabolites.a: 1}) b2c = Reaction("b2c") b2c.add_metabolites({m.metabolites.b: -1, m.metabolites.c: 1}) dmc = Reaction("DM_c") dmc.add_metabolites({m.metabolites.c: -1}) m.add_reactions([exa, b2c, dmc]) m.objective = 'DM_c' universal = Model() a2b = Reaction("a2b") a2d = Reaction("a2d") universal.add_reactions([a2b, a2d]) a2b.build_reaction_from_string("a --> b", verbose=False) a2d.build_reaction_from_string("a --> d", verbose=False) # # GrowMatch # result = gapfilling.growMatch(m, universal)[0] result = gapfilling.gapfill(m, universal)[0] assert len(result) == 1 assert result[0].id == "a2b" # # SMILEY # result = gapfilling.SMILEY(m, "b", universal)[0] with m: m.objective = m.add_boundary(m.metabolites.b, type='demand') result = gapfilling.gapfill(m, universal)[0] assert len(result) == 1 assert result[0].id == "a2b" # # 2 rounds of GrowMatch with exchange reactions # result = gapfilling.growMatch(m, None, ex_rxns=True, iterations=2) result = gapfilling.gapfill(m, None, exchange_reactions=True, iterations=2) assert len(result) == 2 assert len(result[0]) == 1 assert len(result[1]) == 1 assert {i[0].id for i in result} == {"EX_b", "EX_c"} # somewhat bigger model universal = Model("universal_reactions") with salmonella as model: for i in [i.id for i in model.metabolites.f6p_c.reactions]: reaction = model.reactions.get_by_id(i) universal.add_reactions([reaction.copy()]) model.remove_reactions([reaction]) gf = gapfilling.GapFiller(model, universal, penalties={'TKT2': 1e3}, demand_reactions=False) solution = gf.fill() assert 'TKT2' not in {r.id for r in solution[0]} assert gf.validate(solution[0])
def test_gapfilling(salmonella): """Test Gapfilling.""" m = Model() m.add_metabolites([Metabolite(m_id) for m_id in ["a", "b", "c"]]) exa = Reaction("EX_a") exa.add_metabolites({m.metabolites.a: 1}) b2c = Reaction("b2c") b2c.add_metabolites({m.metabolites.b: -1, m.metabolites.c: 1}) dmc = Reaction("DM_c") dmc.add_metabolites({m.metabolites.c: -1}) m.add_reactions([exa, b2c, dmc]) m.objective = 'DM_c' universal = Model() a2b = Reaction("a2b") a2d = Reaction("a2d") universal.add_reactions([a2b, a2d]) a2b.build_reaction_from_string("a --> b", verbose=False) a2d.build_reaction_from_string("a --> d", verbose=False) # # GrowMatch # result = gapfilling.growMatch(m, universal)[0] result = gapfill(m, universal)[0] assert len(result) == 1 assert result[0].id == "a2b" # # SMILEY # result = gapfilling.SMILEY(m, "b", universal)[0] with m: m.objective = m.add_boundary(m.metabolites.b, type='demand') result = gapfill(m, universal)[0] assert len(result) == 1 assert result[0].id == "a2b" # # 2 rounds of GrowMatch with exchange reactions # result = gapfilling.growMatch(m, None, ex_rxns=True, iterations=2) result = gapfill(m, None, exchange_reactions=True, iterations=2) assert len(result) == 2 assert len(result[0]) == 1 assert len(result[1]) == 1 assert {i[0].id for i in result} == {"EX_b", "EX_c"} # # Gapfilling solution adds metabolites not present in original model # test for when demand = T # a demand reaction must be added to clear new metabolite universal_noDM = Model() a2b = Reaction("a2b") universal_noDM.add_reactions([a2b]) a2b.build_reaction_from_string("a --> b + d", verbose=False) result = gapfill(m, universal_noDM, exchange_reactions=False, demand_reactions=True)[0] # add reaction a2b and demand reaction to clear met d assert len(result) == 2 assert "a2b" in [x.id for x in result] # test for when demand = False # test for when metabolites are added to the model and # must be cleared by other reactions in universal model # (i.e. not necessarily a demand reaction) universal_withDM = universal_noDM.copy() d_dm = Reaction("d_dm") universal_withDM.add_reactions([d_dm]) d_dm.build_reaction_from_string("d -->", verbose=False) result = gapfill(m, universal_withDM, exchange_reactions=False, demand_reactions=False)[0] assert len(result) == 2 assert "a2b" in [x.id for x in result] # somewhat bigger model universal = Model("universal_reactions") with salmonella as model: for i in [i.id for i in model.metabolites.f6p_c.reactions]: reaction = model.reactions.get_by_id(i) universal.add_reactions([reaction.copy()]) model.remove_reactions([reaction]) gf = GapFiller(model, universal, penalties={'TKT2': 1e3}, demand_reactions=False) solution = gf.fill() assert 'TKT2' not in {r.id for r in solution[0]} assert gf.validate(solution[0])
# You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import sys, re, os, glob from cobra.core import Model from cobra.io import read_sbml_model,write_sbml_model folder = sys.argv[1] metamodel_id = sys.argv[2] assert os.path.isdir(folder) metamodel = Model(metamodel_id) metamodel.description = metamodel_id reactions = set() models = [] for fname in glob.glob(os.path.join(folder,"*.xml")): model = read_sbml_model(fname) models.append(model) print "%s loaded" % model.id for r in model.reactions: r.id = re.sub('_[ec][0-9]','',r.id) if r.id in reactions: continue metamodel.add_reaction(r.copy()) reactions.add(r.id)
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 test_gapfilling(self): try: get_solver_name(mip=True) except SolverNotFound: pytest.skip("no MILP solver found") m = Model() m.add_metabolites(map(Metabolite, ["a", "b", "c"])) r = Reaction("EX_A") m.add_reaction(r) r.add_metabolites({m.metabolites.a: 1}) r = Reaction("r1") m.add_reaction(r) r.add_metabolites({m.metabolites.b: -1, m.metabolites.c: 1}) r = Reaction("DM_C") m.add_reaction(r) r.add_metabolites({m.metabolites.c: -1}) r.objective_coefficient = 1 U = Model() r = Reaction("a2b") U.add_reaction(r) r.build_reaction_from_string("a --> b", verbose=False) r = Reaction("a2d") U.add_reaction(r) r.build_reaction_from_string("a --> d", verbose=False) # GrowMatch result = gapfilling.growMatch(m, U)[0] assert len(result) == 1 assert result[0].id == "a2b" # SMILEY result = gapfilling.SMILEY(m, "b", U)[0] assert len(result) == 1 assert result[0].id == "a2b" # 2 rounds of GrowMatch with exchange reactions result = gapfilling.growMatch(m, None, ex_rxns=True, iterations=2) assert len(result) == 2 assert len(result[0]) == 1 assert len(result[1]) == 1 assert {i[0].id for i in result} == {"SMILEY_EX_b", "SMILEY_EX_c"}