예제 #1
0
def _fva_step(reaction_id):
    global _model
    global _loopless
    rxn = _model.reactions.get_by_id(reaction_id)
    # The previous objective assignment already triggers a reset
    # so directly update coefs here to not trigger redundant resets
    # in the history manager which can take longer than the actual
    # FVA for small models
    _model.solver.objective.set_linear_coefficients({
        rxn.forward_variable: 1,
        rxn.reverse_variable: -1
    })
    _model.slim_optimize()
    sutil.check_solver_status(_model.solver.status)
    if _loopless:
        value = loopless_fva_iter(_model, rxn)
    else:
        value = _model.solver.objective.value
    # handle infeasible case
    if value is None:
        value = float("nan")
        LOGGER.warning(
            "Could not get flux for reaction %s,  setting "
            "it to NaN. This is usually due to numerical instability.",
            rxn.id,
        )
    _model.solver.objective.set_linear_coefficients({
        rxn.forward_variable: 0,
        rxn.reverse_variable: 0
    })
    return reaction_id, value
예제 #2
0
def _fva_optlang(model, reaction_list, fraction, loopless):
    """Helper function to perform FVA with the optlang interface.

    Parameters
    ----------
    model : a cobra model
    reaction_list : list of reactions

    Returns
    -------
    dict
        A dictionary containing the results.
    """
    fva_results = {str(rxn): {} for rxn in reaction_list}
    prob = model.problem
    with model as m:
        m.solver.optimize()
        if m.solver.status != "optimal":
            raise ValueError("There is no optimal solution "
                             "for the chosen objective!")
        # Add objective as a variable to the model than set to zero
        # This also uses the fraction to create the lower bound for the
        # old objective
        fva_old_objective = prob.Variable(
            "fva_old_objective", lb=fraction * m.solver.objective.value)
        fva_old_obj_constraint = prob.Constraint(
            m.solver.objective.expression - fva_old_objective, lb=0, ub=0,
            name="fva_old_objective_constraint")
        m.add_cons_vars([fva_old_objective, fva_old_obj_constraint])
        model.objective = S.Zero  # This will trigger the reset as well
        for what in ("minimum", "maximum"):
            sense = "min" if what == "minimum" else "max"
            for rxn in reaction_list:
                r_id = str(rxn)
                rxn = m.reactions.get_by_id(r_id)
                # The previous objective assignment already triggers a reset
                # so directly update coefs here to not trigger redundant resets
                # in the history manager which can take longer than the actual
                # FVA for small models
                m.solver.objective.set_linear_coefficients(
                    {rxn.forward_variable: 1, rxn.reverse_variable: -1})
                m.solver.objective.direction = sense
                m.solver.optimize()
                sutil.check_solver_status(m.solver.status)
                if loopless:
                    value = loopless_fva_iter(m, rxn)
                else:
                    value = m.solver.objective.value
                fva_results[r_id][what] = value
                m.solver.objective.set_linear_coefficients(
                    {rxn.forward_variable: 0, rxn.reverse_variable: 0})

    return fva_results
예제 #3
0
def _fva_step(reaction_id):
    global _model
    global _loopless
    rxn = _model.reactions.get_by_id(reaction_id)
    # The previous objective assignment already triggers a reset
    # so directly update coefs here to not trigger redundant resets
    # in the history manager which can take longer than the actual
    # FVA for small models
    _model.solver.objective.set_linear_coefficients({
        rxn.forward_variable: 1,
        rxn.reverse_variable: -1
    })
    _model.slim_optimize()
    sutil.check_solver_status(_model.solver.status)
    if _loopless:
        value = loopless_fva_iter(_model, rxn)
    else:
        value = _model.solver.objective.value
    _model.solver.objective.set_linear_coefficients({
        rxn.forward_variable: 0,
        rxn.reverse_variable: 0
    })
    return reaction_id, value
예제 #4
0
def flux_variability_analysis(model,
                              reaction_list=None,
                              loopless=False,
                              fraction_of_optimum=1.0,
                              pfba_factor=None):
    """
    Determine the minimum and maximum possible flux value for each reaction.

    Parameters
    ----------
    model : cobra.Model
        The model for which to run the analysis. It will *not* be modified.
    reaction_list : list of cobra.Reaction or str, optional
        The reactions for which to obtain min/max fluxes. If None will use
        all reactions in the model (default).
    loopless : boolean, optional
        Whether to return only loopless solutions. This is significantly
        slower. Please also refer to the notes.
    fraction_of_optimum : float, optional
        Must be <= 1.0. Requires that the objective value is at least the
        fraction times maximum objective value. A value of 0.85 for instance
        means that the objective has to be at least at 85% percent of its
        maximum.
    pfba_factor : float, optional
        Add an additional constraint to the model that requires the total sum
        of absolute fluxes must not be larger than this value times the
        smallest possible sum of absolute fluxes, i.e., by setting the value
        to 1.1 the total sum of absolute fluxes must not be more than
        10% larger than the pFBA solution. Since the pFBA solution is the
        one that optimally minimizes the total flux sum, the ``pfba_factor``
        should, if set, be larger than one. Setting this value may lead to
        more realistic predictions of the effective flux bounds.

    Returns
    -------
    pandas.DataFrame
        A data frame with reaction identifiers as the index and two columns:
        - maximum: indicating the highest possible flux
        - minimum: indicating the lowest possible flux

    Notes
    -----
    This implements the fast version as described in [1]_. Please note that
    the flux distribution containing all minimal/maximal fluxes does not have
    to be a feasible solution for the model. Fluxes are minimized/maximized
    individually and a single minimal flux might require all others to be
    suboptimal.

    Using the loopless option will lead to a significant increase in
    computation time (about a factor of 100 for large models). However, the
    algorithm used here (see [2]_) is still more than 1000x faster than the
    "naive" version using ``add_loopless(model)``. Also note that if you have
    included constraints that force a loop (for instance by setting all fluxes
    in a loop to be non-zero) this loop will be included in the solution.

    References
    ----------
    .. [1] Computationally efficient flux variability analysis.
       Gudmundsson S, Thiele I.
       BMC Bioinformatics. 2010 Sep 29;11:489.
       doi: 10.1186/1471-2105-11-489, PMID: 20920235

    .. [2] CycleFreeFlux: efficient removal of thermodynamically infeasible
       loops from flux distributions.
       Desouki AA, Jarre F, Gelius-Dietrich G, Lercher MJ.
       Bioinformatics. 2015 Jul 1;31(13):2159-65.
       doi: 10.1093/bioinformatics/btv096.
    """
    if reaction_list is None:
        reaction_list = model.reactions
    else:
        reaction_list = model.reactions.get_by_any(reaction_list)

    prob = model.problem
    fva_results = DataFrame(
        {
            "minimum": zeros(len(reaction_list), dtype=float),
            "maximum": zeros(len(reaction_list), dtype=float)
        },
        index=[r.id for r in reaction_list])
    with model:
        # Safety check before setting up FVA.
        model.slim_optimize(error_value=None,
                            message="There is no optimal solution for the "
                            "chosen objective!")
        # Add the previous objective as a variable to the model then set it to
        # zero. This also uses the fraction to create the lower/upper bound for
        # the old objective.
        if model.solver.objective.direction == "max":
            fva_old_objective = prob.Variable("fva_old_objective",
                                              lb=fraction_of_optimum *
                                              model.solver.objective.value)
        else:
            fva_old_objective = prob.Variable("fva_old_objective",
                                              ub=fraction_of_optimum *
                                              model.solver.objective.value)
        fva_old_obj_constraint = prob.Constraint(
            model.solver.objective.expression - fva_old_objective,
            lb=0,
            ub=0,
            name="fva_old_objective_constraint")
        model.add_cons_vars([fva_old_objective, fva_old_obj_constraint])

        if pfba_factor is not None:
            if pfba_factor < 1.:
                warn("The 'pfba_factor' should be larger or equal to 1.",
                     UserWarning)
            with model:
                add_pfba(model, fraction_of_optimum=0)
                ub = model.slim_optimize(error_value=None)
                flux_sum = prob.Variable("flux_sum", ub=pfba_factor * ub)
                flux_sum_constraint = prob.Constraint(
                    model.solver.objective.expression - flux_sum,
                    lb=0,
                    ub=0,
                    name="flux_sum_constraint")
            model.add_cons_vars([flux_sum, flux_sum_constraint])

        model.objective = Zero  # This will trigger the reset as well
        for what in ("minimum", "maximum"):
            sense = "min" if what == "minimum" else "max"
            model.solver.objective.direction = sense
            for rxn in reaction_list:
                # The previous objective assignment already triggers a reset
                # so directly update coefs here to not trigger redundant resets
                # in the history manager which can take longer than the actual
                # FVA for small models
                model.solver.objective.set_linear_coefficients({
                    rxn.forward_variable:
                    1,
                    rxn.reverse_variable:
                    -1
                })
                model.slim_optimize()
                sutil.check_solver_status(model.solver.status)
                if loopless:
                    value = loopless_fva_iter(model, rxn)
                else:
                    value = model.solver.objective.value
                fva_results.at[rxn.id, what] = value
                model.solver.objective.set_linear_coefficients({
                    rxn.forward_variable:
                    0,
                    rxn.reverse_variable:
                    0
                })

    return fva_results[["minimum", "maximum"]]
예제 #5
0
def flux_variability_analysis(model, reaction_list=None, loopless=False,
                              fraction_of_optimum=1.0, pfba_factor=None):
    """
    Determine the minimum and maximum possible flux value for each reaction.

    Parameters
    ----------
    model : cobra.Model
        The model for which to run the analysis. It will *not* be modified.
    reaction_list : list of cobra.Reaction or str, optional
        The reactions for which to obtain min/max fluxes. If None will use
        all reactions in the model (default).
    loopless : boolean, optional
        Whether to return only loopless solutions. This is significantly
        slower. Please also refer to the notes.
    fraction_of_optimum : float, optional
        Must be <= 1.0. Requires that the objective value is at least the
        fraction times maximum objective value. A value of 0.85 for instance
        means that the objective has to be at least at 85% percent of its
        maximum.
    pfba_factor : float, optional
        Add an additional constraint to the model that requires the total sum
        of absolute fluxes must not be larger than this value times the
        smallest possible sum of absolute fluxes, i.e., by setting the value
        to 1.1 the total sum of absolute fluxes must not be more than
        10% larger than the pFBA solution. Since the pFBA solution is the
        one that optimally minimizes the total flux sum, the ``pfba_factor``
        should, if set, be larger than one. Setting this value may lead to
        more realistic predictions of the effective flux bounds.

    Returns
    -------
    pandas.DataFrame
        A data frame with reaction identifiers as the index and two columns:
        - maximum: indicating the highest possible flux
        - minimum: indicating the lowest possible flux

    Notes
    -----
    This implements the fast version as described in [1]_. Please note that
    the flux distribution containing all minimal/maximal fluxes does not have
    to be a feasible solution for the model. Fluxes are minimized/maximized
    individually and a single minimal flux might require all others to be
    suboptimal.

    Using the loopless option will lead to a significant increase in
    computation time (about a factor of 100 for large models). However, the
    algorithm used here (see [2]_) is still more than 1000x faster than the
    "naive" version using ``add_loopless(model)``. Also note that if you have
    included constraints that force a loop (for instance by setting all fluxes
    in a loop to be non-zero) this loop will be included in the solution.

    References
    ----------
    .. [1] Computationally efficient flux variability analysis.
       Gudmundsson S, Thiele I.
       BMC Bioinformatics. 2010 Sep 29;11:489.
       doi: 10.1186/1471-2105-11-489, PMID: 20920235

    .. [2] CycleFreeFlux: efficient removal of thermodynamically infeasible
       loops from flux distributions.
       Desouki AA, Jarre F, Gelius-Dietrich G, Lercher MJ.
       Bioinformatics. 2015 Jul 1;31(13):2159-65.
       doi: 10.1093/bioinformatics/btv096.
    """
    if reaction_list is None:
        reaction_list = model.reactions
    else:
        reaction_list = model.reactions.get_by_any(reaction_list)

    prob = model.problem
    fva_results = DataFrame({
        "minimum": zeros(len(reaction_list), dtype=float),
        "maximum": zeros(len(reaction_list), dtype=float)
    }, index=[r.id for r in reaction_list])
    with model:
        # Safety check before setting up FVA.
        model.slim_optimize(error_value=None,
                            message="There is no optimal solution for the "
                                    "chosen objective!")
        # Add the previous objective as a variable to the model then set it to
        # zero. This also uses the fraction to create the lower/upper bound for
        # the old objective.
        if model.solver.objective.direction == "max":
            fva_old_objective = prob.Variable(
                "fva_old_objective",
                lb=fraction_of_optimum * model.solver.objective.value)
        else:
            fva_old_objective = prob.Variable(
                "fva_old_objective",
                ub=fraction_of_optimum * model.solver.objective.value)
        fva_old_obj_constraint = prob.Constraint(
            model.solver.objective.expression - fva_old_objective, lb=0, ub=0,
            name="fva_old_objective_constraint")
        model.add_cons_vars([fva_old_objective, fva_old_obj_constraint])

        if pfba_factor is not None:
            if pfba_factor < 1.:
                warn("The 'pfba_factor' should be larger or equal to 1.",
                     UserWarning)
            with model:
                add_pfba(model, fraction_of_optimum=0)
                ub = model.slim_optimize(error_value=None)
                flux_sum = prob.Variable("flux_sum", ub=pfba_factor * ub)
                flux_sum_constraint = prob.Constraint(
                    model.solver.objective.expression - flux_sum, lb=0, ub=0,
                    name="flux_sum_constraint")
            model.add_cons_vars([flux_sum, flux_sum_constraint])

        model.objective = Zero  # This will trigger the reset as well
        for what in ("minimum", "maximum"):
            sense = "min" if what == "minimum" else "max"
            for rxn in reaction_list:
                # The previous objective assignment already triggers a reset
                # so directly update coefs here to not trigger redundant resets
                # in the history manager which can take longer than the actual
                # FVA for small models
                model.solver.objective.set_linear_coefficients(
                    {rxn.forward_variable: 1, rxn.reverse_variable: -1})
                model.solver.objective.direction = sense
                model.slim_optimize()
                sutil.check_solver_status(model.solver.status)
                if loopless:
                    value = loopless_fva_iter(model, rxn)
                else:
                    value = model.solver.objective.value
                fva_results.at[rxn.id, what] = value
                model.solver.objective.set_linear_coefficients(
                    {rxn.forward_variable: 0, rxn.reverse_variable: 0})

    return fva_results
예제 #6
0
def _fva_optlang(model, reaction_list, fraction, loopless, pfba_factor):
    """Helper function to perform FVA with the optlang interface.

    Parameters
    ----------
    model : a cobra model
    reaction_list : list of reactions
    fraction : float, optional
        Must be <= 1.0. Requires that the objective value is at least
        fraction * max_objective_value. A value of 0.85 for instance means that
        the objective has to be at least at 85% percent of its maximum.
    loopless : boolean, optional
        Whether to return only loopless solutions.
    pfba_factor : float, optional
        Add additional constraint to the model that the total sum of
        absolute fluxes must not be larger than this value times the
        smallest possible sum of absolute fluxes, i.e., by setting the value
        to 1.1 then the total sum of absolute fluxes must not be more than
        10% larger than the pfba solution. Setting this value may lead to
        more realistic predictions of the effective flux bounds.

    Returns
    -------
    dict
        A dictionary containing the results.
    """
    prob = model.problem
    fva_results = {rxn.id: {} for rxn in reaction_list}
    with model as m:
        m.slim_optimize(error_value=None,
                        message="There is no optimal solution for the "
                                "chosen objective!")
        # Add objective as a variable to the model than set to zero
        # This also uses the fraction to create the lower bound for the
        # old objective
        fva_old_objective = prob.Variable(
            "fva_old_objective", lb=fraction * m.solver.objective.value)
        fva_old_obj_constraint = prob.Constraint(
            m.solver.objective.expression - fva_old_objective, lb=0, ub=0,
            name="fva_old_objective_constraint")
        m.add_cons_vars([fva_old_objective, fva_old_obj_constraint])

        if pfba_factor is not None:
            if pfba_factor < 1.:
                warn('pfba_factor should be larger or equal to 1', UserWarning)
            with m:
                add_pfba(m, fraction_of_optimum=0)
                ub = m.slim_optimize(error_value=None)
                flux_sum = prob.Variable("flux_sum", ub=pfba_factor * ub)
                flux_sum_constraint = prob.Constraint(
                    m.solver.objective.expression - flux_sum, lb=0, ub=0,
                    name="flux_sum_constraint")
            m.add_cons_vars([flux_sum, flux_sum_constraint])

        m.objective = Zero  # This will trigger the reset as well
        for what in ("minimum", "maximum"):
            sense = "min" if what == "minimum" else "max"
            for rxn in reaction_list:
                r_id = rxn.id
                rxn = m.reactions.get_by_id(r_id)
                # The previous objective assignment already triggers a reset
                # so directly update coefs here to not trigger redundant resets
                # in the history manager which can take longer than the actual
                # FVA for small models
                m.solver.objective.set_linear_coefficients(
                    {rxn.forward_variable: 1, rxn.reverse_variable: -1})
                m.solver.objective.direction = sense
                m.slim_optimize()
                sutil.check_solver_status(m.solver.status)
                if loopless:
                    value = loopless_fva_iter(m, rxn)
                else:
                    value = m.solver.objective.value
                fva_results[r_id][what] = value
                m.solver.objective.set_linear_coefficients(
                    {rxn.forward_variable: 0, rxn.reverse_variable: 0})

    return fva_results
예제 #7
0
def _fva_optlang(model, reaction_list, fraction, loopless, pfba_factor):
    """Helper function to perform FVA with the optlang interface.

    Parameters
    ----------
    model : a cobra model
    reaction_list : list of reactions
    fraction : float, optional
        Must be <= 1.0. Requires that the objective value is at least
        fraction * max_objective_value. A value of 0.85 for instance means that
        the objective has to be at least at 85% percent of its maximum.
    loopless : boolean, optional
        Whether to return only loopless solutions.
    pfba_factor : float, optional
        Add additional constraint to the model that the total sum of
        absolute fluxes must not be larger than this value times the
        smallest possible sum of absolute fluxes, i.e., by setting the value
        to 1.1 then the total sum of absolute fluxes must not be more than
        10% larger than the pfba solution. Setting this value may lead to
        more realistic predictions of the effective flux bounds.

    Returns
    -------
    dict
        A dictionary containing the results.
    """
    prob = model.problem
    fva_results = {rxn.id: {} for rxn in reaction_list}
    with model as m:
        m.slim_optimize(error_value=None,
                        message="There is no optimal solution for the "
                        "chosen objective!")
        # Add objective as a variable to the model than set to zero
        # This also uses the fraction to create the lower bound for the
        # old objective
        fva_old_objective = prob.Variable("fva_old_objective",
                                          lb=fraction *
                                          m.solver.objective.value)
        fva_old_obj_constraint = prob.Constraint(
            m.solver.objective.expression - fva_old_objective,
            lb=0,
            ub=0,
            name="fva_old_objective_constraint")
        m.add_cons_vars([fva_old_objective, fva_old_obj_constraint])

        if pfba_factor is not None:
            if pfba_factor < 1.:
                warn('pfba_factor should be larger or equal to 1', UserWarning)
            with m:
                add_pfba(m, fraction_of_optimum=0)
                ub = m.slim_optimize(error_value=None)
                flux_sum = prob.Variable("flux_sum", ub=pfba_factor * ub)
                flux_sum_constraint = prob.Constraint(
                    m.solver.objective.expression - flux_sum,
                    lb=0,
                    ub=0,
                    name="flux_sum_constraint")
            m.add_cons_vars([flux_sum, flux_sum_constraint])

        m.objective = Zero  # This will trigger the reset as well
        for what in ("minimum", "maximum"):
            sense = "min" if what == "minimum" else "max"
            for rxn in reaction_list:
                r_id = rxn.id
                rxn = m.reactions.get_by_id(r_id)
                # The previous objective assignment already triggers a reset
                # so directly update coefs here to not trigger redundant resets
                # in the history manager which can take longer than the actual
                # FVA for small models
                m.solver.objective.set_linear_coefficients({
                    rxn.forward_variable:
                    1,
                    rxn.reverse_variable:
                    -1
                })
                m.solver.objective.direction = sense
                m.slim_optimize()
                sutil.check_solver_status(m.solver.status)
                if loopless:
                    value = loopless_fva_iter(m, rxn)
                else:
                    value = m.solver.objective.value
                fva_results[r_id][what] = value
                m.solver.objective.set_linear_coefficients({
                    rxn.forward_variable:
                    0,
                    rxn.reverse_variable:
                    0
                })

    return fva_results