def _build_problem(self, essential_reactions, use_nullspace_simplification): logger.debug("Starting to formulate OptKnock problem") self.essential_reactions = find_essential_reactions(self._model).union( self._model.exchanges) if essential_reactions: self.essential_reactions.update( set( get_reaction_for(self._model, r) for r in essential_reactions)) reactions = set(self._model.reactions) - self.essential_reactions if use_nullspace_simplification: reactions = self._reduce_to_nullspace(reactions) else: self.reaction_groups = None self._make_dual() self._combine_primal_and_dual() logger.debug("Primal and dual successfully combined") y_vars = {} constrained_dual_vars = set() for reaction in reactions: if reaction not in self.essential_reactions and reaction.lower_bound <= 0 <= reaction.upper_bound: y_var, constrained_vars = self._add_knockout_constraints( reaction) y_vars[y_var] = reaction constrained_dual_vars.update(constrained_vars) self._y_vars = y_vars primal_objective = self._model.solver.objective dual_objective = self._model.solver.interface.Objective.clone( self._dual_problem.objective, model=self._model.solver) reduced_expression = Add(*((c * v) for v, c in dual_objective. expression.as_coefficients_dict().items() if v not in constrained_dual_vars)) dual_objective = self._model.solver.interface.Objective( reduced_expression, direction=dual_objective.direction) optimality_constraint = self._model.solver.interface.Constraint( primal_objective.expression - dual_objective.expression, lb=0, ub=0, name="inner_optimality") self._model.solver.add(optimality_constraint) logger.debug("Inner optimality constrained") logger.debug("Adding constraint for number of knockouts") knockout_number_constraint = self._model.solver.interface.Constraint( Add(*y_vars), lb=len(y_vars), ub=len(y_vars)) self._model.solver.add(knockout_number_constraint) self._number_of_knockouts_constraint = knockout_number_constraint
def run(self, target=None, max_enforced_flux=0.9, number_of_results=10, exclude=(), simulation_method=fba, simulation_kwargs=None): """ Performs a Flux Scanning based on Enforced Objective Flux (FSEOF) analysis. Parameters ---------- target: str, Reaction, Metabolite The target for optimization. max_enforced_flux : float, optional The maximal flux of secondary_objective that will be enforced, relative to the theoretical maximum ( defaults to 0.9). number_of_results : int, optional The number of enforced flux levels (defaults to 10). exclude : Iterable of reactions or reaction ids that will not be included in the output. Returns ------- FseofResult An object containing the identified reactions and the used parameters. References ---------- .. [1] H. S. Choi, S. Y. Lee, T. Y. Kim, and H. M. Woo, 'In silico identification of gene amplification targets for improvement of lycopene production.,' Appl Environ Microbiol, vol. 76, no. 10, pp. 3097–3105, May 2010. """ model = self.model target = get_reaction_for(model, target) simulation_kwargs = simulation_kwargs if simulation_kwargs is not None else {} simulation_kwargs['objective'] = self.primary_objective if 'reference' not in simulation_kwargs: reference = simulation_kwargs['reference'] = pfba(model, **simulation_kwargs) else: reference = simulation_kwargs['reference'] ndecimals = config.ndecimals # Exclude list exclude = list(exclude) + model.boundary exclude_ids = [target.id] for reaction in exclude: if isinstance(reaction, Reaction): exclude_ids.append(reaction.id) else: exclude_ids.append(reaction) with TimeMachine() as tm: tm(do=int, undo=partial(setattr, model, "objective", model.objective)) tm(do=int, undo=partial(setattr, target, "lower_bound", target.lower_bound)) tm(do=int, undo=partial(setattr, target, "upper_bound", target.upper_bound)) # Find initial flux of enforced reaction initial_fluxes = reference.fluxes initial_flux = round(initial_fluxes[target.id], ndecimals) # Find theoretical maximum of enforced reaction max_theoretical_flux = round(fba(model, objective=target.id, reactions=[target.id]).fluxes[target.id], ndecimals) max_flux = max_theoretical_flux * max_enforced_flux # Calculate enforcement levels levels = [initial_flux + (i + 1) * (max_flux - initial_flux) / number_of_results for i in range(number_of_results)] # FSEOF results results = {reaction.id: [] for reaction in model.reactions} for level in levels: target.lower_bound = level target.upper_bound = level solution = simulation_method(model, **simulation_kwargs) for reaction_id, flux in solution.fluxes.iteritems(): results[reaction_id].append(round(flux, ndecimals)) # Test each reaction fseof_reactions = [] for reaction_id, fluxes in results.items(): if reaction_id not in exclude_ids \ and max(abs(max(fluxes)), abs(min(fluxes))) > abs(reference[reaction_id]) \ and min(fluxes) * max(fluxes) >= 0: fseof_reactions.append(model.reactions.get_by_id(reaction_id)) results = {rea.id: results[rea.id] for rea in fseof_reactions} run_args = dict(max_enforced_flux=max_enforced_flux, number_of_results=number_of_results, solution_method=simulation_method, simulation_kwargs=simulation_kwargs, exclude=exclude) return FSEOFResult(fseof_reactions, target, model, self.primary_objective, levels, results, run_args, reference)
def run(self, target=None, biomass=None, substrate=None, max_knockouts=5, variable_size=True, simulation_method=fba, growth_coupled=False, max_evaluations=20000, population_size=200, max_results=50, use_nullspace_simplification=True, seed=None, **kwargs): """ Parameters ---------- target : str, Metabolite or Reaction The design target biomass : str, Metabolite or Reaction The biomass definition in the model substrate : str, Metabolite or Reaction The main carbon source max_knockouts : int Max number of knockouts allowed variable_size : bool If true, all candidates have the same size. Otherwise the candidate size can be from 1 to max_knockouts. simulation_method : function Any method from cameo.flux_analysis.simulation or equivalent growth_coupled : bool If true will use the minimum flux rate to compute the fitness max_evaluations : int Number of evaluations before stop population_size : int Number of individuals in each generation max_results : int Max number of different designs to return if found. kwargs : dict Arguments for the simulation method. seed : int A seed for random. use_nullspace_simplification : Boolean (default True) Use a basis for the nullspace to find groups of reactions whose fluxes are multiples of each other and dead end reactions. From each of these groups only 1 reaction will be included as a possible knockout. Returns ------- OptGeneResult """ target = get_reaction_for(self._model, target) biomass = get_reaction_for(self._model, biomass) substrate = get_reaction_for(self._model, substrate) if growth_coupled: objective_function = biomass_product_coupled_min_yield( biomass, target, substrate) else: objective_function = biomass_product_coupled_yield( biomass, target, substrate) if self.manipulation_type == "genes": optimization_algorithm = GeneKnockoutOptimization( model=self._model, heuristic_method=self._algorithm, essential_genes=self._essential_genes, plot=self.plot, objective_function=objective_function, use_nullspace_simplification=use_nullspace_simplification) elif self.manipulation_type == "reactions": optimization_algorithm = ReactionKnockoutOptimization( model=self._model, heuristic_method=self._algorithm, essential_reactions=self._essential_reactions, plot=self.plot, objective_function=objective_function, use_nullspace_simplification=use_nullspace_simplification) else: raise ValueError("Invalid manipulation type %s" % self.manipulation_type) optimization_algorithm.simulation_kwargs = kwargs optimization_algorithm.simulation_method = simulation_method optimization_algorithm.archiver = ProductionStrainArchive() result = optimization_algorithm.run(max_evaluations=max_evaluations, pop_size=population_size, max_size=max_knockouts, variable_size=variable_size, maximize=True, max_archive_size=max_results, seed=seed, **kwargs) kwargs.update(optimization_algorithm.simulation_kwargs) return OptGeneResult(self._model, result, objective_function, simulation_method, self.manipulation_type, biomass, target, substrate, kwargs)
def run(self, target=None, biomass=None, substrate=None, max_swaps=5, variable_size=True, simulation_method=fba, growth_coupled=False, max_evaluations=20000, population_size=200, time_machine=None, max_results=50, seed=None, **kwargs): """ Parameters ---------- target : str, Metabolite or Reaction The design target. biomass : str, Metabolite or Reaction The biomass definition in the model. substrate : str, Metabolite or Reaction The main carbon source. max_swaps : int Max number of swaps allowed. variable_size : bool If true, all candidates have the same size. Otherwise the candidate size can be from 1 to max_knockouts. simulation_method : function Any method from cameo.flux_analysis.simulation or equivalent. growth_coupled : bool If true will use the minimum flux rate to compute the fitness. max_evaluations : int Number of evaluations before stop. population_size : int Number of individuals in each generation. time_machine : TimeMachine See TimeMachine. max_results : int Max number of different designs to return if found. kwargs : dict Arguments for the simulation method. seed : int A seed for random. Returns ------- HeuristicOptSwapResult """ target = get_reaction_for(self._model, target) biomass = get_reaction_for(self._model, biomass) substrate = get_reaction_for(self._model, substrate) if growth_coupled: objective_function = biomass_product_coupled_min_yield( biomass, target, substrate) else: objective_function = biomass_product_coupled_yield( biomass, target, substrate) optimization_algorithm = CofactorSwapOptimization( model=self._model, cofactor_id_swaps=self._cofactor_id_swaps, skip_reactions=self._skip_reactions, objective_function=objective_function) optimization_algorithm.simulation_kwargs = kwargs optimization_algorithm.simulation_method = simulation_method optimization_algorithm.archiver = ProductionStrainArchive() result = optimization_algorithm.run(max_evaluations=max_evaluations, pop_size=population_size, max_size=max_swaps, variable_size=variable_size, maximize=True, max_archive_size=max_results, seed=seed, **kwargs) kwargs.update(optimization_algorithm.simulation_kwargs) return HeuristicOptSwapResult(self._model, result, self._swap_pairs, objective_function, simulation_method, biomass, target, substrate, kwargs)
def phenotypic_phase_plane(model, variables, objective=None, source=None, points=20, view=None): """Phenotypic phase plane analysis [1]. Implements a phenotypic phase plan analysis with interpretation same as that presented in [1] but calculated by optimizing the model for all steps of the indicated variables (instead of using shadow prices). Parameters ---------- model: cobra.Model variables: str or reaction or iterable A reaction ID, reaction, or list of reactions to be varied. objective: str or reaction or optlang.Objective or Metabolite, optional An objective, a reaction's flux, or a metabolite's production to be minimized/maximized (defaults to the current model objective). source: Reaction or reaction identifier The reaction to use as the source when calculating mass and carbon yield. Set to the medium reaction with the highest input carbon flux if left None. points: int or iterable Number of points to be interspersed between the variable bounds. A list of same same dimensions as `variables` can be used to specify variable specific numbers of points. view: SequentialView or MultiprocessingView or ipython.cluster.DirectView A parallelization view. Returns ------- PhenotypicPhasePlaneResult The phenotypic phase plane with flux, mol carbon input / mol carbon output, gram input / gram output References ---------- [1] Edwards, J. S., Ramakrishna, R. and Palsson, B. O. (2002). Characterizing the metabolic phenotype: a phenotype phase plane analysis. Biotechnology and Bioengineering, 77(1), 27–36. doi:10.1002/bit.10047 """ if isinstance(variables, six.string_types): variables = [variables] elif isinstance(variables, Reaction): variables = [variables] variable_ids = [ var if isinstance(var, six.string_types) else var.id for var in variables ] if view is None: view = config.default_view with model: if objective is not None: if isinstance(objective, Metabolite): try: objective = model.reactions.get_by_id("DM_%s" % objective.id) except KeyError: objective = model.add_boundary(objective, type='demand') # try: # objective = model.reaction_for(objective, time_machine=tm) # except KeyError: # pass model.objective = objective if source is not None: source_reaction = get_reaction_for(model, source) else: source_reaction = get_c_source_reaction(model) variable_reactions = model.reactions.get_by_any(variables) variables_min_max = flux_variability_analysis( model, reactions=variable_reactions, view=SequentialView()) grid = [ numpy.linspace(lower_bound, upper_bound, points, endpoint=True) for reaction_id, lower_bound, upper_bound in variables_min_max.data_frame.loc[variable_ids].itertuples() ] grid_generator = itertools.product(*grid) chunks_of_points = partition(list(grid_generator), len(view)) evaluator = _PhenotypicPhasePlaneChunkEvaluator( model, variable_reactions, source_reaction) chunk_results = view.map(evaluator, chunks_of_points) envelope = reduce(list.__add__, chunk_results) nice_variable_ids = [_nice_id(reaction) for reaction in variable_reactions] variable_reactions_ids = [reaction.id for reaction in variable_reactions] phase_plane = pandas.DataFrame( envelope, columns=(variable_reactions_ids + [ 'objective_lower_bound', 'objective_upper_bound', 'c_yield_lower_bound', 'c_yield_upper_bound', 'mass_yield_lower_bound', 'mass_yield_upper_bound' ])) if objective is None: objective = model.objective nice_objective_id = _nice_id(objective) objective = objective.id if isinstance(objective, Reaction) else str(objective) return PhenotypicPhasePlaneResult( phase_plane, variable_reactions_ids, objective, nice_variable_ids=nice_variable_ids, source_reaction=_nice_id(source_reaction), nice_objective_id=nice_objective_id)
def run(self, max_knockouts=5, biomass=None, target=None, max_results=1, *args, **kwargs): """ Perform the OptKnock simulation Parameters ---------- target: str, Metabolite or Reaction The design target biomass: str, Metabolite or Reaction The biomass definition in the model max_knockouts: int Max number of knockouts allowed max_results: int Max number of different designs to return if found Returns ------- OptKnockResult """ # TODO: why not required arguments? if biomass is None or target is None: raise ValueError('missing biomass and/or target reaction') target = get_reaction_for(self._model, target, add=False) biomass = get_reaction_for(self._model, biomass, add=False) knockout_list = [] fluxes_list = [] production_list = [] biomass_list = [] loader_id = ui.loading() with self._model: self._model.objective = target.id self._number_of_knockouts_constraint.lb = self._number_of_knockouts_constraint.ub - max_knockouts count = 0 while count < max_results: try: solution = self._model.optimize(raise_error=True) except OptimizationError as e: logger.debug( "Problem could not be solved. Terminating and returning " + str(count) + " solutions") logger.debug(str(e)) break knockouts = tuple(reaction for y, reaction in self._y_vars.items() if round(y.primal, 3) == 0) assert len(knockouts) <= max_knockouts if self.reaction_groups: combinations = decompose_reaction_groups( self.reaction_groups, knockouts) for kos in combinations: knockout_list.append({r.id for r in kos}) fluxes_list.append(solution.fluxes) production_list.append(solution.f) biomass_list.append(solution.fluxes[biomass.id]) else: knockout_list.append({r.id for r in knockouts}) fluxes_list.append(solution.fluxes) production_list.append(solution.f) biomass_list.append(solution.fluxes[biomass.id]) # Add an integer cut y_vars_to_cut = [ y for y in self._y_vars if round(y.primal, 3) == 0 ] integer_cut = self._model.solver.interface.Constraint( Add(*y_vars_to_cut), lb=1, name="integer_cut_" + str(count)) if len(knockouts) < max_knockouts: self._number_of_knockouts_constraint.lb = self._number_of_knockouts_constraint.ub - len( knockouts) self._model.add_cons_vars(integer_cut) count += 1 ui.stop_loader(loader_id) return OptKnockResult(self._original_model, knockout_list, fluxes_list, production_list, biomass_list, target.id, biomass)
def run(self, target=None, max_enforced_flux=0.9, number_of_results=10, exclude=(), simulation_method=fba, simulation_kwargs=None): """ Performs a Flux Scanning based on Enforced Objective Flux (FSEOF) analysis. Parameters ---------- target: str, Reaction, Metabolite The target for optimization. max_enforced_flux : float, optional The maximal flux of secondary_objective that will be enforced, relative to the theoretical maximum ( defaults to 0.9). number_of_results : int, optional The number of enforced flux levels (defaults to 10). exclude : Iterable of reactions or reaction ids that will not be included in the output. Returns ------- FseofResult An object containing the identified reactions and the used parameters. References ---------- .. [1] H. S. Choi, S. Y. Lee, T. Y. Kim, and H. M. Woo, 'In silico identification of gene amplification targets for improvement of lycopene production.,' Appl Environ Microbiol, vol. 76, no. 10, pp. 3097–3105, May 2010. """ model = self.model target = get_reaction_for(model, target) simulation_kwargs = simulation_kwargs if simulation_kwargs is not None else {} simulation_kwargs['objective'] = self.primary_objective if 'reference' not in simulation_kwargs: reference = simulation_kwargs['reference'] = pfba( model, **simulation_kwargs) else: reference = simulation_kwargs['reference'] ndecimals = config.ndecimals # Exclude list exclude = list(exclude) + model.exchanges exclude_ids = [target.id] for reaction in exclude: if isinstance(reaction, Reaction): exclude_ids.append(reaction.id) else: exclude_ids.append(reaction) with TimeMachine() as tm: tm(do=int, undo=partial(setattr, model, "objective", model.objective)) tm(do=int, undo=partial(setattr, target, "lower_bound", target.lower_bound)) tm(do=int, undo=partial(setattr, target, "upper_bound", target.upper_bound)) # Find initial flux of enforced reaction initial_fluxes = reference.fluxes initial_flux = round(initial_fluxes[target.id], ndecimals) # Find theoretical maximum of enforced reaction max_theoretical_flux = round( fba(model, objective=target.id, reactions=[target.id]).fluxes[target.id], ndecimals) max_flux = max_theoretical_flux * max_enforced_flux # Calculate enforcement levels levels = [ initial_flux + (i + 1) * (max_flux - initial_flux) / number_of_results for i in range(number_of_results) ] # FSEOF results results = {reaction.id: [] for reaction in model.reactions} for level in levels: target.lower_bound = level target.upper_bound = level solution = simulation_method(model, **simulation_kwargs) for reaction_id, flux in solution.fluxes.iteritems(): results[reaction_id].append(round(flux, ndecimals)) # Test each reaction fseof_reactions = [] for reaction_id, fluxes in results.items(): if reaction_id not in exclude_ids \ and max(abs(max(fluxes)), abs(min(fluxes))) > abs(reference[reaction_id]) \ and min(fluxes) * max(fluxes) >= 0: fseof_reactions.append(model.reactions.get_by_id(reaction_id)) results = {rea.id: results[rea.id] for rea in fseof_reactions} run_args = dict(max_enforced_flux=max_enforced_flux, number_of_results=number_of_results, solution_method=simulation_method, simulation_kwargs=simulation_kwargs, exclude=exclude) return FSEOFResult(fseof_reactions, target, model, self.primary_objective, levels, results, run_args, reference)
def run(self, target=None, biomass=None, substrate=None, max_knockouts=5, variable_size=True, simulation_method=fba, growth_coupled=False, max_evaluations=20000, population_size=200, max_results=50, use_nullspace_simplification=True, seed=None, **kwargs): """ Parameters ---------- target : str, Metabolite or Reaction The design target biomass : str, Metabolite or Reaction The biomass definition in the model substrate : str, Metabolite or Reaction The main carbon source max_knockouts : int Max number of knockouts allowed variable_size : bool If true, all candidates have the same size. Otherwise the candidate size can be from 1 to max_knockouts. simulation_method : function Any method from cameo.flux_analysis.simulation or equivalent growth_coupled : bool If true will use the minimum flux rate to compute the fitness max_evaluations : int Number of evaluations before stop population_size : int Number of individuals in each generation max_results : int Max number of different designs to return if found. kwargs : dict Arguments for the simulation method. seed : int A seed for random. use_nullspace_simplification : Boolean (default True) Use a basis for the nullspace to find groups of reactions whose fluxes are multiples of each other and dead end reactions. From each of these groups only 1 reaction will be included as a possible knockout. Returns ------- OptGeneResult """ target = get_reaction_for(self._model, target) biomass = get_reaction_for(self._model, biomass) substrate = get_reaction_for(self._model, substrate) if growth_coupled: objective_function = biomass_product_coupled_min_yield(biomass, target, substrate) else: objective_function = biomass_product_coupled_yield(biomass, target, substrate) if self.manipulation_type == "genes": optimization_algorithm = GeneKnockoutOptimization( model=self._model, heuristic_method=self._algorithm, essential_genes=self._essential_genes, plot=self.plot, objective_function=objective_function, use_nullspace_simplification=use_nullspace_simplification) elif self.manipulation_type == "reactions": optimization_algorithm = ReactionKnockoutOptimization( model=self._model, heuristic_method=self._algorithm, essential_reactions=self._essential_reactions, plot=self.plot, objective_function=objective_function, use_nullspace_simplification=use_nullspace_simplification) else: raise ValueError("Invalid manipulation type %s" % self.manipulation_type) optimization_algorithm.simulation_kwargs = kwargs optimization_algorithm.simulation_method = simulation_method optimization_algorithm.archiver = ProductionStrainArchive() result = optimization_algorithm.run(max_evaluations=max_evaluations, pop_size=population_size, max_size=max_knockouts, variable_size=variable_size, maximize=True, max_archive_size=max_results, seed=seed, **kwargs) kwargs.update(optimization_algorithm.simulation_kwargs) return OptGeneResult(self._model, result, objective_function, simulation_method, self.manipulation_type, biomass, target, substrate, kwargs)
def run(self, target=None, biomass=None, substrate=None, max_swaps=5, variable_size=True, simulation_method=fba, growth_coupled=False, max_evaluations=20000, population_size=200, time_machine=None, max_results=50, seed=None, **kwargs): """ Parameters ---------- target : str, Metabolite or Reaction The design target. biomass : str, Metabolite or Reaction The biomass definition in the model. substrate : str, Metabolite or Reaction The main carbon source. max_swaps : int Max number of swaps allowed. variable_size : bool If true, all candidates have the same size. Otherwise the candidate size can be from 1 to max_knockouts. simulation_method : function Any method from cameo.flux_analysis.simulation or equivalent. growth_coupled : bool If true will use the minimum flux rate to compute the fitness. max_evaluations : int Number of evaluations before stop. population_size : int Number of individuals in each generation. time_machine : TimeMachine See TimeMachine. max_results : int Max number of different designs to return if found. kwargs : dict Arguments for the simulation method. seed : int A seed for random. Returns ------- HeuristicOptSwapResult """ target = get_reaction_for(self._model, target) biomass = get_reaction_for(self._model, biomass) substrate = get_reaction_for(self._model, substrate) if growth_coupled: objective_function = biomass_product_coupled_min_yield(biomass, target, substrate) else: objective_function = biomass_product_coupled_yield(biomass, target, substrate) optimization_algorithm = CofactorSwapOptimization(model=self._model, cofactor_id_swaps=self._cofactor_id_swaps, skip_reactions=self._skip_reactions, objective_function=objective_function) optimization_algorithm.simulation_kwargs = kwargs optimization_algorithm.simulation_method = simulation_method optimization_algorithm.archiver = ProductionStrainArchive() result = optimization_algorithm.run(max_evaluations=max_evaluations, pop_size=population_size, max_size=max_swaps, variable_size=variable_size, maximize=True, max_archive_size=max_results, seed=seed, **kwargs) kwargs.update(optimization_algorithm.simulation_kwargs) return HeuristicOptSwapResult(self._model, result, self._swap_pairs, objective_function, simulation_method, biomass, target, substrate, kwargs)