Source code for cobra.flux_analysis.parsimonious

# -*- coding: utf-8 -*-

from __future__ import absolute_import

import logging
from warnings import warn
from itertools import chain

import sympy

from cobra.util import solver as sutil
from cobra.manipulation.modify import (
    convert_to_irreversible, revert_to_reversible)
from cobra.util import linear_reaction_coefficients, set_objective
from cobra.core.solution import get_solution

add = sympy.Add._from_args
mul = sympy.Mul._from_args
LOGGER = logging.getLogger(__name__)


[docs]def optimize_minimal_flux(*args, **kwargs): warn("optimize_minimal_flux has been renamed to pfba", DeprecationWarning) return pfba(*args, **kwargs)
[docs]def pfba(model, already_irreversible=False, fraction_of_optimum=1.0, solver=None, desired_objective_value=None, objective=None, reactions=None, **optimize_kwargs): """Perform basic pFBA (parsimonious Enzyme Usage Flux Balance Analysis) to minimize total flux. pFBA [1] adds the minimization of all fluxes the the objective of the model. This approach is motivated by the idea that high fluxes have a higher enzyme turn-over and that since producing enzymes is costly, the cell will try to minimize overall flux while still maximizing the original objective function, e.g. the growth rate. Parameters ---------- model : cobra.Model The model already_irreversible : bool, optional By default, the model is converted to an irreversible one. However, if the model is already irreversible, this step can be skipped. Ignored for optlang solvers as not relevant. fraction_of_optimum : float, optional Fraction of optimum which must be maintained. The original objective reaction is constrained to be greater than maximal_value * fraction_of_optimum. solver : str, optional Name of the solver to be used. If None it will respect the solver set in the model (model.solver). desired_objective_value : float, optional A desired objective value for the minimal solution that bypasses the initial optimization result. Ignored for optlang solvers, instead, define your objective separately and pass using the `objective` argument. objective : dict or model.problem.Objective A desired objective to use during optimization in addition to the pFBA objective. Dictionaries (reaction as key, coefficient as value) can be used for linear objectives. Not used for non-optlang solvers. reactions : iterable List of reactions or reaction identifiers. Implies `return_frame` to be true. Only return fluxes for the given reactions. Faster than fetching all fluxes if only a few are needed. Only supported for optlang solvers. **optimize_kwargs : additional arguments for legacy solver, optional Additional arguments passed to the legacy solver. Ignored for optlang solver (those can be configured using model.solver.configuration). Returns ------- cobra.Solution The solution object to the optimized model with pFBA constraints added. References ---------- .. [1] Lewis, N. E., Hixson, K. K., Conrad, T. M., Lerman, J. A., Charusanti, P., Polpitiya, A. D., Palsson, B. O. (2010). Omic data from evolved E. coli are consistent with computed optimal growth from genome-scale models. Molecular Systems Biology, 6, 390. doi:10.1038/msb.2010.47 """ legacy, solver = sutil.choose_solver(model, solver) if legacy: return _pfba_legacy( model, already_irreversible=already_irreversible, fraction_of_optimum=fraction_of_optimum, solver=solver, desired_objective_value=desired_objective_value, **optimize_kwargs) else: model.solver = solver return _pfba_optlang( model, objective=objective, fraction_of_optimum=fraction_of_optimum, reactions=reactions)
[docs]def add_pfba(model, objective=None, fraction_of_optimum=1.0): """Add pFBA objective Add objective to minimize the summed flux of all reactions to the current objective. See Also ------- pfba Parameters ---------- model : cobra.Model The model to add the objective to objective : An objective to set in combination with the pFBA objective. fraction_of_optimum : float Fraction of optimum which must be maintained. The original objective reaction is constrained to be greater than maximal_value * fraction_of_optimum. """ if objective is not None: model.objective = objective if model.solver.objective.name == '_pfba_objective': raise ValueError('model already has pfba objective') sutil.fix_objective_as_constraint(model, fraction=fraction_of_optimum) reaction_variables = ((rxn.forward_variable, rxn.reverse_variable) for rxn in model.reactions) variables = chain(*reaction_variables) pfba_objective = model.problem.Objective(add( [mul((sympy.singleton.S.One, variable)) for variable in variables]), direction='min', sloppy=True, name="_pfba_objective") set_objective(model, pfba_objective)
def _pfba_optlang(model, objective=None, reactions=None, fraction_of_optimum=1.0): """Helper function to perform pFBA with the optlang interface Not meant to be used directly. Parameters ---------- model : a cobra model The model to perform pFBA on objective : An objective to use in addition to the pFBA constraints. reactions : iterable List of reactions or reaction identifiers. Returns ------- cobra.Solution The solution to the pFBA optimization. Updates everything in-place, returns model to original state at end. """ reactions = model.reactions if reactions is None \ else model.reactions.get_by_any(reactions) with model as m: add_pfba(m, objective=objective, fraction_of_optimum=fraction_of_optimum) m.slim_optimize(error_value=None) solution = get_solution(m, reactions=reactions) return solution def _pfba_legacy(model, solver, already_irreversible=False, fraction_of_optimum=1.0, desired_objective_value=None, **optimize_kwargs): """Perform basic pFBA (parsimonious FBA) and minimize total flux. The function attempts to act as a drop-in replacement for optimize. It will make the reaction reversible and perform an optimization, then force the objective value to remain the same and minimize the total flux. Finally, it will convert the reaction back to the irreversible form it was in before. See http://dx.doi.org/10.1038/msb.2010.47 Parameters ---------- model : cobra.Model The model solver : solver The solver object to use already_irreversible : bool, optional By default, the model is converted to an irreversible one. However, if the model is already irreversible, this step can be skipped fraction_of_optimum : float, optional Fraction of optimum which must be maintained. The original objective reaction is constrained to be greater than maximal_value * fraction_of_optimum. By default, this option is specified to be 1.0 desired_objective_value : float, optional A desired objective value for the minimal solution that bypasses the initial optimization result. Updates everything in-place, returns model to original state at end. """ objective_reactions = linear_reaction_coefficients(model) if len(objective_reactions) > 1: raise ValueError('pfba only supports models with' ' a single objective function') if 'objective_sense' in optimize_kwargs: if optimize_kwargs['objective_sense'] == 'minimize': raise ValueError( 'Minimization not supported in pfba') optimize_kwargs.pop('objective_sense', None) if not already_irreversible: convert_to_irreversible(model) lp = solver.create_problem(model, **optimize_kwargs) if not desired_objective_value: solver.solve_problem(lp, objective_sense='maximize') status = solver.get_status(lp) if status != "optimal": revert_to_reversible(model) raise ValueError( "pFBA requires optimal solution status, not {}".format(status)) desired_objective_value = solver.get_objective_value(lp) for i, reaction in enumerate(model.reactions): if reaction.objective_coefficient != 0: # Enforce a certain fraction of the original objective target = (desired_objective_value * fraction_of_optimum / reaction.objective_coefficient) solver.change_variable_bounds(lp, i, target, reaction.upper_bound) # Minimize all reaction fluxes (including objective?) solver.change_variable_objective(lp, i, 1) solver.solve_problem(lp, objective_sense='minimize', **optimize_kwargs) solution = solver.format_solution(lp, model) # Return the model to its original state # model.solution = solution revert_to_reversible(model) # if solution.status == "optimal": # model.solution.f = sum([coeff * reaction.x for reaction, coeff in # iteritems(objective_reactions)]) return solution