def test_reaction_down_regulation_target(self, model): reaction_id = "PGI" ref_val = 4.86 value = 3.4 # (B - A) / A fold_change = -0.30041 down_reg_target = ReactionModulationTarget(reaction_id, value, ref_val) assert round(abs(down_reg_target.fold_change - fold_change), 5) == 0 with TimeMachine() as tm: down_reg_target.apply(model, time_machine=tm) assert model.reactions.PGI.upper_bound == 3.4 assert model.reactions.PGI.lower_bound == -1000 assert abs(model.solve().f - 0.8706) < 0.0001 assert model.reactions.PGI.upper_bound == 1000 assert model.reactions.PGI.lower_bound == -1000 reaction_id = "RPI" ref_val = -2.28150 value = -1.5 fold_change = -0.342537 down_reg_target = ReactionModulationTarget(reaction_id, value, ref_val) assert round(abs(down_reg_target.fold_change - fold_change), 5) == 0 with TimeMachine() as tm: down_reg_target.apply(model, time_machine=tm) assert model.reactions.RPI.lower_bound == -1.5 assert model.reactions.RPI.upper_bound == 1000 assert abs(model.solve().f - 0.8691) < 0.0001 assert model.reactions.RPI.lower_bound == -1000 assert model.reactions.RPI.upper_bound == 1000
def test_reaction_cofactor_swap_target(self, model): cofactor_id_swaps = [("nad_c", "nadh_c"), ("nadp_c", "nadph_c")] swap_pairs = ([ model.metabolites.get_by_id(m) for m in cofactor_id_swaps[0] ], [model.metabolites.get_by_id(m) for m in cofactor_id_swaps[1]]) swap_target = ReactionCofactorSwapTarget("GAPD", swap_pairs) with TimeMachine() as tm: swap_target.apply(model, time_machine=tm) assert model.metabolites.nad_c not in model.reactions.GAPD.metabolites assert model.metabolites.nadh_c not in model.reactions.GAPD.metabolites assert model.metabolites.nadp_c in model.reactions.GAPD.metabolites assert model.metabolites.nadph_c in model.reactions.GAPD.metabolites assert model.metabolites.nadp_c not in model.reactions.GAPD.metabolites assert model.metabolites.nadph_c not in model.reactions.GAPD.metabolites assert model.metabolites.nad_c in model.reactions.GAPD.metabolites assert model.metabolites.nadh_c in model.reactions.GAPD.metabolites swap_target = ReactionCofactorSwapTarget("GND", swap_pairs) with TimeMachine() as tm: swap_target.apply(model, time_machine=tm) assert model.metabolites.nad_c in model.reactions.GND.metabolites assert model.metabolites.nadh_c in model.reactions.GND.metabolites assert model.metabolites.nadp_c not in model.reactions.GND.metabolites assert model.metabolites.nadph_c not in model.reactions.GND.metabolites assert model.metabolites.nadp_c in model.reactions.GND.metabolites assert model.metabolites.nadph_c in model.reactions.GND.metabolites assert model.metabolites.nad_c not in model.reactions.GND.metabolites assert model.metabolites.nadh_c not in model.reactions.GND.metabolites
def test_add_remove_pfb(self, core_model): with TimeMachine() as tm: add_pfba(core_model, time_machine=tm) assert '_pfba_objective' == core_model.objective.name assert '_pfba_objective' != core_model.solver.constraints with TimeMachine() as tm: fix_pfba_as_constraint(core_model, time_machine=tm) assert '_fixed_pfba_constraint' in core_model.solver.constraints assert '_fixed_pfba_constraint' not in core_model.solver.constraints
def test_reactions_in_group_become_blocked_if_one_is_removed( self, core_model): essential_reactions = core_model.essential_reactions() coupled_reactions = structural.find_coupled_reactions_nullspace( core_model) for group in coupled_reactions: representative = pick_one(group) if representative not in essential_reactions: with TimeMachine() as tm: assert core_model == representative.model tm(do=partial(core_model.remove_reactions, [representative], delete=False), undo=partial(core_model.add_reactions, [representative])) # # FIXME: Hack because of optlang queue issues with GLPK # core_model.solver.update() assert representative not in core_model.reactions assert representative.forward_variable not in core_model.solver.variables assert representative.reverse_variable not in core_model.solver.variables assert representative not in core_model.reactions assert representative.model is None blocked_reactions = find_blocked_reactions(core_model) assert all(r in blocked_reactions for r in group if r != representative) assert representative in core_model.reactions coupled_reactions = structural.find_coupled_reactions(core_model) for group in coupled_reactions: representative = pick_one(group) if representative not in essential_reactions: with TimeMachine() as tm: fwd_var_name = representative.forward_variable.name rev_var_name = representative.reverse_variable.name assert core_model == representative.model tm(do=partial(core_model.remove_reactions, [representative], delete=False), undo=partial(core_model.add_reactions, [representative])) # # FIXME: Hack because of optlang queue issues with GLPK # core_model.solver.update() assert representative not in core_model.reactions assert fwd_var_name not in core_model.solver.variables assert rev_var_name not in core_model.solver.variables assert representative not in core_model.reactions assert representative.model is None blocked_reactions = find_blocked_reactions(core_model) assert representative not in core_model.reactions assert all(r in blocked_reactions for r in group if r != representative) assert representative in core_model.reactions
def test_moma_shlomi_2005_change_ref(self, toy_model): if current_solver_name(toy_model) == 'glpk': pytest.skip('glpk does not support qp') original_objective = toy_model.objective reference = { "b1": 10, "v1": 10, "v2": 5, "v3": 0, "v4": 0, "v5": 0, "v6": 5, "b2": 5, "b3": 5 } expected = { 'b1': 8.8, 'b2': 4.4, 'b3': 4.4, 'v1': 8.8, 'v2': 3.1, 'v3': 1.3, 'v4': 4.4, 'v5': 3.1, 'v6': 0.0 } with TimeMachine() as tm: toy_model.reactions.v6.knock_out(tm) result = moma(toy_model, reference=reference) for k in reference.keys(): assert abs(expected[k] - result.fluxes[k]) < 0.1, "%s: %f | %f" assert toy_model.objective is original_objective reference_changed = { "b1": 5, "v1": 5, "v2": 5, "v3": 0, "v4": 0, "v5": 0, "v6": 5, "b2": 5, "b3": 5 } with TimeMachine() as tm: toy_model.reactions.v6.knock_out(tm) result_changed = moma(toy_model, reference=reference_changed) assert expected != result_changed.fluxes
def test_weird_left_to_right_reaction_issue(self): model = Model("Toy Model") m1 = Metabolite("M1") d1 = Reaction("ex1") d1.add_metabolites({m1: -1}) d1.upper_bound = 0 d1.lower_bound = -1000 # print d1.reaction, d1.lower_bound, d1.upper_bound model.add_reactions([d1]) self.assertFalse(d1.reversibility) self.assertEqual(d1.lower_bound, -1000) self.assertEqual(d1._lower_bound, -1000) self.assertEqual(d1.upper_bound, 0) self.assertEqual(d1._upper_bound, 0) with TimeMachine() as tm: d1.knock_out(time_machine=tm) self.assertEqual(d1.lower_bound, 0) self.assertEqual(d1._lower_bound, 0) self.assertEqual(d1.upper_bound, 0) self.assertEqual(d1._upper_bound, 0) self.assertEqual(d1.lower_bound, -1000) self.assertEqual(d1._lower_bound, -1000) self.assertEqual(d1.upper_bound, 0) self.assertEqual(d1._upper_bound, 0)
def test_knockout(self): original_bounds = dict() for reaction in self.model.reactions: original_bounds[reaction.id] = (reaction.lower_bound, reaction.upper_bound) reaction.knock_out() self.assertEqual(reaction.lower_bound, 0) self.assertEqual(reaction.upper_bound, 0) for k, (lb, ub) in six.iteritems(original_bounds): self.model.reactions.get_by_id(k).lower_bound = lb self.model.reactions.get_by_id(k).upper_bound = ub for reaction in self.model.reactions: self.assertEqual(reaction.lower_bound, original_bounds[reaction.id][0]) self.assertEqual(reaction.upper_bound, original_bounds[reaction.id][1]) with TimeMachine() as tm: for reaction in self.model.reactions: original_bounds[reaction.id] = (reaction.lower_bound, reaction.upper_bound) reaction.knock_out(time_machine=tm) self.assertEqual(reaction.lower_bound, 0) self.assertEqual(reaction.upper_bound, 0) for reaction in self.model.reactions: self.assertEqual(reaction.lower_bound, original_bounds[reaction.id][0]) self.assertEqual(reaction.upper_bound, original_bounds[reaction.id][1])
def test_room_shlomi_2005(self, toy_model): original_objective = toy_model.objective reference = { "b1": 10, "v1": 10, "v2": 5, "v3": 0, "v4": 0, "v5": 0, "v6": 5, "b2": 5, "b3": 5 } expected = { 'b1': 10.0, 'b2': 5.0, 'b3': 5.0, 'v1': 10.0, 'v2': 5.0, 'v3': 0.0, 'v4': 5.0, 'v5': 5.0, 'v6': 0.0 } with TimeMachine() as tm: toy_model.reactions.v6.knock_out(tm) result = room(toy_model, reference=reference, delta=0, epsilon=0) for k in reference.keys(): assert abs(expected[k] - result.fluxes[k]) < 0.1, "%s: %f | %f" assert toy_model.objective is original_objective
def test_with_statement(self): l = [1, 2, 3, 4] with TimeMachine() as tm: tm(do=partial(l.append, 33), undo=partial(l.pop)) tm(do=partial(l.append, 66), undo=partial(l.pop)) tm(do=partial(l.append, 99), undo=partial(l.pop)) assert l == [1, 2, 3, 4]
def __call__(self, strategy): points = 50 surface_only = False if self.debug: points = 5 surface_only = True (model, pathway, aerobic) = (strategy[1], strategy[2], strategy[3]) model = model.copy() assert isinstance(model, SolverBasedModel) assert isinstance(pathway, PathwayResult) assert isinstance(aerobic, bool) with TimeMachine() as tm: if not aerobic and 'EX_o2_e' in model.reactions: model.reactions.EX_o2_e.change_bounds(lb=0, time_machine=tm) pathway.apply(model, tm) model.objective = model.biomass diff_fva = DifferentialFVA(design_space_model=model, objective=pathway.product.id, variables=[model.biomass], points=points) designs = diff_fva.run(improvements_only=True, surface_only=surface_only) return designs
def _production_envelope_inner(self, point): tm = TimeMachine() try: for (reaction, coordinate) in zip(self.variable_reactions, point): tm(do=partial(setattr, reaction, 'lower_bound', coordinate), undo=partial(setattr, reaction, 'lower_bound', reaction.lower_bound)) tm(do=partial(setattr, reaction, 'upper_bound', coordinate), undo=partial(setattr, reaction, 'upper_bound', reaction.upper_bound)) interval = [] tm(do=int, undo=partial(setattr, self.model.objective, 'direction', self.model.objective.direction)) self.model.objective.direction = 'min' try: solution = self.model.solve().f except Infeasible: solution = 0 interval.append(solution) self.model.objective.direction = 'max' try: solution = self.model.solve().f except Infeasible: solution = 0 interval.append(solution) finally: tm.reset() return point + tuple(interval)
def _process_knockouts(self): progress = ProgressBar( maxval=len(self._knockouts), widgets=["Processing solutions: ", Bar(), Percentage()]) self._processed_knockouts = DataFrame(columns=[ "reactions", "size", self._target, "biomass", "fva_min", "fva_max" ]) for i, knockouts in progress(enumerate(self._knockouts)): try: with TimeMachine() as tm: [ self._model.reactions.get_by_id(ko).knock_out( time_machine=tm) for ko in knockouts ] fva = flux_variability_analysis(self._model, fraction_of_optimum=0.99, reactions=[self.target]) self._processed_knockouts.loc[i] = [ knockouts, len(knockouts), self.production[i], self.biomass[i], fva.lower_bound(self.target), fva.upper_bound(self.target) ] except SolveError: self._processed_knockouts.loc[i] = [ numpy.nan for _ in self._processed_knockouts.columns ]
def test_room_shlomi_2005(self): reference = { "b1": -10, "v1": 10, "v2": 5, "v3": 0, "v4": 0, "v5": 0, "v6": 5, "b2": 5, "b3": 5 } TOY_MODEL_PAPIN_2004.solver = self.model.solver.interface with TimeMachine() as tm: TOY_MODEL_PAPIN_2004.reactions.v6.knock_out(tm) result = room(TOY_MODEL_PAPIN_2004, reference=reference, delta=0, epsilon=0) self.assertEquals( result.fluxes, { 'b1': 10.0, 'b2': 5.0, 'b3': 5.0, 'v1': 5.0, 'v2': 5.0, 'v3': 0.0, 'v4': 5.0, 'v5': 5.0, 'v6': 0.0 })
def _simulate(self, reactions): with TimeMachine() as tm: for reaction in reactions: reaction.knock_out(time_machine=tm) solution = self.simulation_method(self.model, reference=self.reference) return solution
def display_on_map(self, index=0, map_name=None, palette="YlGnBu"): with TimeMachine() as tm: for ko in self.data_frame.loc[index, "reactions"]: self._model.reactions.get_by_id(ko).knock_out(tm) fluxes = self._simulation_method(self._model, **self._simulation_kwargs) fluxes.display_on_map(map_name=map_name, palette=palette)
def _production_envelope_inner(self, point): with TimeMachine() as tm: for (reaction, coordinate) in zip(self.variable_reactions, point): reaction.change_bounds(coordinate, coordinate, time_machine=tm) interval = [] interval_carbon_yield = [] interval_mass_yield = [] tm(do=int, undo=partial(setattr, self.model.objective, 'direction', self.model.objective.direction)) self.model.objective.direction = 'min' flux, carbon_yield, mass_yield = self._interval_estimates() interval.append(flux) interval_carbon_yield.append(carbon_yield) interval_mass_yield.append(mass_yield) self.model.objective.direction = 'max' flux, carbon_yield, mass_yield = self._interval_estimates() interval.append(flux) interval_carbon_yield.append(carbon_yield) interval_mass_yield.append(mass_yield) intervals = tuple(interval) + tuple(interval_carbon_yield) + tuple( interval_mass_yield) return point + intervals
def evaluate_individual(self, individual): """ Evaluates a single individual. Arguments --------- individual: set The encoded representation of a single individual. Returns ------- fitness A single real value or a Pareto, depending on the number of objectives. """ targets = self.decoder(individual) with TimeMachine() as tm: for target in targets: target.knock_out(time_machine=tm) try: solution = self.simulation_method( self.model, cache=self.cache, volatile=False, raw=True, reactions=self.objective_function.reactions, **self.simulation_kwargs) fitness = self.objective_function(self.model, solution, targets) except SolveError as e: logger.debug(e) fitness = self.objective_function.worst_fitness() return fitness
def find_blocked_reactions(model): """Determine reactions that cannot carry steady-state flux. Parameters ---------- model: SolverBasedModel Returns ------- list A list of reactions. """ with TimeMachine() as tm: for exchange in model.exchanges: tm(do=partial(setattr, exchange, 'lower_bound', -999999), undo=partial(setattr, exchange, 'lower_bound', exchange.lower_bound)) tm(do=partial(setattr, exchange, 'upper_bound', 999999), undo=partial(setattr, exchange, 'upper_bound', exchange.upper_bound)) fva_solution = flux_variability_analysis(model) return [ model.reactions.get_by_id(id) for id in fva_solution.data_frame.query( 'upper_bound == lower_bound == 0').index ]
def essential_reactions(self, threshold=1e-6): """Return a list of essential reactions. Parameters ---------- threshold : float (default 1e-6) Minimal objective flux to be considered viable. Returns ------- list List of essential reactions """ essential = [] try: solution = self.solve() for reaction_id, flux in six.iteritems(solution.fluxes): if abs(flux) > 0: reaction = self.reactions.get_by_id(reaction_id) with TimeMachine() as tm: reaction.knock_out(time_machine=tm) try: sol = self.solve() except Infeasible: essential.append(reaction) else: if sol.f < threshold: essential.append(reaction) except SolveError as e: logger.error('Cannot determine essential reactions for un-optimal model.') raise e return essential
def test_one_change_list(self): tm = TimeMachine() l = [1, 2, 3, 4] tm(do=partial(l.append, 5), undo=l.pop) assert l == [1, 2, 3, 4, 5] tm.reset() assert l == [1, 2, 3, 4]
def fba(model, objective=None, reactions=None, *args, **kwargs): """Flux Balance Analysis. Parameters ---------- model: SolverBasedModel objective: a valid objective - see SolverBaseModel.objective (optional) Returns ------- FluxDistributionResult Contains the result of the linear solver. """ with TimeMachine() as tm: if objective is not None: tm(do=partial(setattr, model, 'objective', objective), undo=partial(setattr, model, 'objective', model.objective)) solution = model.solve() if reactions is not None: result = FluxDistributionResult( {r: solution.get_primal_by_id(r) for r in reactions}, solution.f) else: result = FluxDistributionResult.from_solution(solution) return result
def __call__(self, strategy): max_evaluations = 20000 if self.debug: max_evaluations = 1000 (model, pathway, aerobic) = (strategy[1], strategy[2], strategy[3]) model = model.copy() assert isinstance(model, SolverBasedModel) assert isinstance(pathway, PathwayResult) assert isinstance(aerobic, bool) with TimeMachine() as tm: if not aerobic and 'EX_o2_e' in model.reactions: model.reactions.EX_o2_e.change_bounds(lb=0, time_machine=tm) pathway.apply(model, tm) model.objective = model.biomass opt_gene = OptGene(model=model, plot=False) designs = opt_gene.run(target=pathway.product.id, biomass=model.biomass, substrate=model.carbon_source, max_evaluations=max_evaluations, max_knockouts=15) return designs
def process_gene_knockout_solution(model, solution, simulation_method, simulation_kwargs, biomass, target, substrate, objective_function): """ Arguments --------- model: SolverBasedModel A constraint-based model solution: tuple The genes simulation_method: function See see cameo.flux_analysis.simulation simulation_kwargs: dict Keyword arguments to run the simulation method biomass: Reaction Cellular biomass reaction target: Reaction The strain design target substrate: Reaction The main carbon source uptake rate objective_function: cameo.strain_design.heuristic.evolutionary.objective_functions.ObjectiveFunction The objective function used for evaluation. Returns ------- list A list with: reactions, genes, size, fva_min, fva_max, target flux, biomass flux, yield, fitness """ with TimeMachine() as tm: genes = [model.genes.get_by_id(gid) for gid in solution] reactions = find_gene_knockout_reactions(model, solution) for reaction in reactions: reaction.knock_out(tm) reaction_ids = [r.id for r in reactions] flux_dist = simulation_method(model, reactions=objective_function.reactions, objective=biomass, **simulation_kwargs) tm(do=partial(setattr, model, "objective", biomass), undo=partial(setattr, model, "objective", model.objective)) fva = flux_variability_analysis(model, fraction_of_optimum=0.99, reactions=[target]) target_yield = flux_dist[target] / abs(flux_dist[substrate]) return [ tuple(reaction_ids), solution, len(solution), fva.lower_bound(target), fva.upper_bound(target), flux_dist[target], flux_dist[biomass], target_yield, objective_function(model, flux_dist, genes) ]
def simulate_knockout(self, to_knockout, *args, **kwargs): cache = {"variables": {}, "constraints": {}, "first_run": True} with TimeMachine() as tm: to_knockout.knock_out(tm) return self._simulation_method( self._model, volatile=False, cache=cache, *args, **kwargs)[cache['original_objective']]
def process_reaction_swap_solution(model, solution, simulation_method, simulation_kwargs, biomass, target, substrate, objective_function, swap_pairs): """ Arguments --------- model: SolverBasedModel A constraint-based model solution: tuple - (reactions, knockouts) The output of a decoder simulation_method: function See see cameo.flux_analysis.simulation simulation_kwargs: dict Keyword arguments to run the simulation method biomass: Reaction Cellular biomass reaction target: Reaction The strain design target substrate: Reaction The main carbon source uptake rate objective_function: cameo.strain_design.heuristic.evolutionary.objective_functions.ObjectiveFunction The objective function used for evaluation. swap_pairs: The metabolites to swap Returns ------- list A list with: reactions, size, fva_min, fva_max, target flux, biomass flux, yield, fitness, [fitness, [fitness]] """ with TimeMachine() as tm: reactions = [model.reactions.get_by_id(rid) for rid in solution] for reaction in reactions: reaction.swap_cofactors(tm, swap_pairs) flux_dist = simulation_method(model, reactions=objective_function.reactions, objective=biomass, **simulation_kwargs) tm(do=partial(setattr, model, "objective", biomass), undo=partial(setattr, model, "objective", model.objective)) fva = flux_variability_analysis(model, fraction_of_optimum=0.99, reactions=[target]) target_yield = flux_dist[target] / abs(flux_dist[substrate]) return [ solution, fva.lower_bound(target), fva.upper_bound(target), flux_dist[target], flux_dist[biomass], target_yield, objective_function(model, flux_dist, reactions) ]
def test_str_handles_different_types_of_stored_operations(self): tm = TimeMachine() def normal_function(): pass partial_function = partial(str, 1) tm(do=normal_function, undo=partial_function) assert tm.__str__().split('\n')[2:-1] == ["undo: " + str(str) + " (1,) {}", 'redo: normal_function']
def test_reaction_knockout_target(self, model): knockout_target = ReactionKnockoutTarget("ACALD") with TimeMachine() as tm: knockout_target.apply(model, time_machine=tm) assert model.reactions.ACALD.lower_bound == 0 assert model.reactions.ACALD.upper_bound == 0 assert model.reactions.ACALD.lower_bound == -1000 assert model.reactions.ACALD.upper_bound == 1000
def test_reaction_knockout_target(self): knockout_target = ReactionKnockoutTarget("ACALD") with TimeMachine() as tm: knockout_target.apply(self.model, time_machine=tm) self.assertEqual(self.model.reactions.ACALD.lower_bound, 0) self.assertEqual(self.model.reactions.ACALD.upper_bound, 0) self.assertEqual(self.model.reactions.ACALD.lower_bound, -1000) self.assertEqual(self.model.reactions.ACALD.upper_bound, 1000)
def __call__(self, strategy): (model, pathway) = (strategy[1], strategy[2]) with TimeMachine() as tm: pathway.plug_model(model, tm) opt_gene = DifferentialFVA(design_space_model=model, objective=pathway.product.id) designs = opt_gene.run() return designs, pathway
def flux_variability_analysis(model, reactions=None, fraction_of_optimum=0., pfba_factor=None, remove_cycles=False, view=None): """Flux variability analysis. Parameters ---------- model : cameo.core.SolverBasedModel reactions: None or iterable The list of reaction whose lower and upper bounds should be determined. If `None`, all reactions in `model` will be assessed. fraction_of_optimum : float Fix the objective of the model to a fraction of it's max. Expected to be within [0;1]. Lower values increase variability. pfba_factor : float If not None, fix the total sum of reaction fluxes to its minimum times a factor. Expected to be within [ 1;inf]. Higher factors increase flux variability to a certain point since the bound for the objective is still fixed. remove_cycles : bool If true, apply the CycleFreeFlux algorithm to remove loops from each simulated flux distribution. view: cameo.parallel.SequentialView or cameo.parallel.MultiprocessingView or ipython.cluster.DirectView A parallelization view. Returns ------- pandas.DataFrame Pandas DataFrame containing the results of the flux variability analysis. """ if view is None: view = config.default_view if reactions is None: reactions = model.reactions with TimeMachine() as tm: if fraction_of_optimum > 0.: model.fix_objective_as_constraint(fraction=fraction_of_optimum, time_machine=tm) if pfba_factor is not None: # don't add the objective-constraint again so fraction_of_optimum=0 fix_pfba_as_constraint(model, multiplier=pfba_factor, time_machine=tm, fraction_of_optimum=0) tm(do=int, undo=partial(setattr, model, "objective", model.objective)) reaction_chunks = (chunk for chunk in partition(reactions, len(view))) if remove_cycles: func_obj = _FvaFunctionObject(model, _cycle_free_fva) else: func_obj = _FvaFunctionObject(model, _flux_variability_analysis) chunky_results = view.map(func_obj, reaction_chunks) solution = pandas.concat(chunky_results) return FluxVariabilityResult(solution)