Пример #1
0
def validate_model(model):
    """
    Cobra model validator 
    
    Modified from https://github.com/aebrahim/cobra_sbml_validator (MIT LICENCE)
    """
    errors = []
    warnings = []

    # test mass balance
    for reaction in model.reactions:
        
        balance = reaction.check_mass_balance()
        
        if len(balance):
            warnings.append("reaction '%s' is not balanced for %s" %
                            (reaction.id, ", ".join(sorted(balance))))

    # try solving
    model.optimize()
    solution = get_solution(model)

    if solution.status != "optimal":
        errors.append("model can not be solved (status '%s')" % solution.status)
        return {"errors": errors, "warnings": warnings}

    # if there is no objective, then we know why the objective was low
    if solution.objective_value <= 0:
        errors.append("model can not produce nonzero biomass")
    if solution.objective_value <= 1e-3:
        warnings.append("biomass flux %s too low" % str(solution.objective_value))

    return {"errors": errors, "warnings": warnings, "objective": solution.objective_value}
Пример #2
0
def find_blocked_reactions(model,
                           reaction_list=None,
                           zero_cutoff=1e-9,
                           open_exchanges=False,
                           processes=None):
    """
    Find reactions that cannot carry any flux.

    The question whether or not a reaction is blocked is highly dependent
    on the current exchange reaction settings for a COBRA model. Hence an
    argument is provided to open all exchange reactions.

    Notes
    -----
    Sink and demand reactions are left untouched. Please modify them manually.

    Parameters
    ----------
    model : cobra.Model
        The model to analyze.
    reaction_list : list, optional
        List of reactions to consider, the default includes all model
        reactions.
    zero_cutoff : float, optional
        Flux value which is considered to effectively be zero.
    open_exchanges : bool, optional
        Whether or not to open all exchange reactions to very high flux ranges.
    processes : int, optional
        The number of parallel processes to run. Can speed up the computations
        if the number of reactions is large. If not explicitly
        passed, it will be set from the global configuration singleton.

    Returns
    -------
    list
        List with the identifiers of blocked reactions.
    """
    with model:
        if open_exchanges:
            for reaction in model.exchanges:
                reaction.bounds = (min(reaction.lower_bound,
                                       -1000), max(reaction.upper_bound, 1000))
        if reaction_list is None:
            reaction_list = model.reactions
        # Limit the search space to reactions which have zero flux. If the
        # reactions already carry flux in this solution,
        # then they cannot be blocked.
        model.slim_optimize()
        solution = get_solution(model, reactions=reaction_list)
        reaction_list = solution.fluxes[
            solution.fluxes.abs() < zero_cutoff].index.tolist()
        # Run FVA to find reactions where both the minimal and maximal flux
        # are zero (below the cut off).
        flux_span = flux_variability_analysis(model,
                                              fraction_of_optimum=0.,
                                              reaction_list=reaction_list,
                                              processes=processes)
        return flux_span[flux_span.abs().max(
            axis=1) < zero_cutoff].index.tolist()
Пример #3
0
    def _model_check(model):
        """
            Check a model produces a steady state flux solution
            :return: bool
            """

        status = model.solver.optimize()
        if status == 'infeasible':
            return False

        solution = get_solution(model)
        if solution.objective_value != 0:
            return True
        return False
Пример #4
0
def find_blocked_reactions(model,
                           reaction_list=None,
                           zero_cutoff=1e-9,
                           open_exchanges=False):
    """Finds reactions that cannot carry a flux with the current
    exchange reaction settings for a cobra model, using flux variability
    analysis.

    Parameters
    ----------
    model : cobra.Model
        The model to analyze
    reaction_list : list
        List of reactions to consider, use all if left missing
    zero_cutoff : float
        Flux value which is considered to effectively be zero.
    open_exchanges : bool
        If true, set bounds on exchange reactions to very high values to
        avoid that being the bottle-neck.

    Returns
    -------
    list
        List with the blocked reactions
    """
    with model:
        if open_exchanges:
            for reaction in model.exchanges:
                reaction.bounds = (min(reaction.lower_bound,
                                       -1000), max(reaction.upper_bound, 1000))
        if reaction_list is None:
            reaction_list = model.reactions
        # limit to reactions which are already 0. If the reactions already
        # carry flux in this solution, then they can not be blocked.
        model.slim_optimize()
        solution = get_solution(model, reactions=reaction_list)
        reaction_list = [
            rxn for rxn in reaction_list
            if abs(solution.fluxes[rxn.id]) < zero_cutoff
        ]
        # run fva to find reactions where both max and min are 0
        flux_span = flux_variability_analysis(model,
                                              fraction_of_optimum=0.,
                                              reaction_list=reaction_list)
        return [
            rxn_id for rxn_id, min_max in flux_span.iterrows()
            if max(abs(min_max)) < zero_cutoff
        ]
Пример #5
0
def find_blocked_reactions(model, reaction_list=None,
                           zero_cutoff=1e-9,
                           open_exchanges=False):
    """Finds reactions that cannot carry a flux with the current
    exchange reaction settings for a cobra model, using flux variability
    analysis.

    Parameters
    ----------
    model : cobra.Model
        The model to analyze
    reaction_list : list
        List of reactions to consider, use all if left missing
    zero_cutoff : float
        Flux value which is considered to effectively be zero.
    open_exchanges : bool
        If true, set bounds on exchange reactions to very high values to
        avoid that being the bottle-neck.

    Returns
    -------
    list
        List with the blocked reactions
    """
    with model:
        if open_exchanges:
            for reaction in model.exchanges:
                reaction.bounds = (min(reaction.lower_bound, -1000),
                                   max(reaction.upper_bound, 1000))
        if reaction_list is None:
            reaction_list = model.reactions
        # limit to reactions which are already 0. If the reactions already
        # carry flux in this solution, then they can not be blocked.
        model.slim_optimize()
        solution = get_solution(model, reactions=reaction_list)
        reaction_list = [rxn for rxn in reaction_list if
                         abs(solution.fluxes[rxn.id]) < zero_cutoff]
        # run fva to find reactions where both max and min are 0
        flux_span = flux_variability_analysis(
            model, fraction_of_optimum=0., reaction_list=reaction_list)
        return [rxn_id for rxn_id, min_max in flux_span.iterrows() if
                max(abs(min_max)) < zero_cutoff]
Пример #6
0
def loopless_fva_iter(model, reaction, all_fluxes=False, zero_cutoff=1e-9):
    """Plugin to get a loopless FVA solution from single FVA iteration.

    Assumes the following about `model` and `reaction`:
    1. the model objective is set to be `reaction`
    2. the model has been optimized and contains the minimum/maximum flux for
       `reaction`
    3. the model contains an auxiliary variable called "fva_old_objective"
       denoting the previous objective

    Parameters
    ----------
    model : cobra.Model
        The model to be used.
    reaction : cobra.Reaction
        The reaction currently minimized/maximized.
    all_fluxes : boolean, optional
        Whether to return all fluxes or only the minimum/maximum for
        `reaction`.
    zero_cutoff : positive float, optional
        Cutoff used for loop removal. Fluxes with an absolute value smaller
        than `zero_cutoff` are considered to be zero.

    Returns
    -------
    single float or dict
        Returns the minimized/maximized flux through `reaction` if
        all_fluxes == False (default). Otherwise returns a loopless flux
        solution containing the minimum/maximum flux for `reaction`.
    """
    current = model.solver.objective.value

    # boundary reactions can not be part of cycles
    if reaction.boundary:
        if all_fluxes:
            return get_solution(model).fluxes
        else:
            return current

    # Reset objective to original one and get a loopless solution
    model.solver.objective.set_linear_coefficients({
        model.variables.fva_old_objective:
        1,
        reaction.forward_variable:
        0,
        reaction.reverse_variable:
        0
    })

    loopless = loopless_solution(model)

    # If the previous optimum is maintained in the loopless solution it was
    # loopless and we are done
    if abs(loopless[reaction.id] - current) < zero_cutoff:
        # Reset the objective to the one used in the iteration, walk around
        # the context manager for speed
        model.solver.objective.set_linear_coefficients({
            model.variables.fva_old_objective:
            0,
            reaction.forward_variable:
            1,
            reaction.reverse_variable:
            -1
        })
        if all_fluxes:
            current = loopless
        return current

    # If previous optimum was not in the loopless solution create a new
    # almost loopless solution containing only loops including the current
    # reaction. Than remove all of those loops.
    with model:
        reaction.bounds = (current, current)
        almost_loopless = loopless_solution(model)
        # find the reactions with loops using the current reaction and remove
        # the loops
        for rxn in model.reactions:
            if ((abs(loopless[rxn.id]) < zero_cutoff)
                    and (abs(almost_loopless[rxn.id]) > zero_cutoff)):
                rxn.bounds = (0, 0)

        # Globally reset the objective to the one used in the FVA iteration
        model.solver.objective.set_linear_coefficients({
            model.variables.fva_old_objective:
            0,
            reaction.forward_variable:
            1,
            reaction.reverse_variable:
            -1
        })

    solution = model.optimize(objective_sense=None)
    if all_fluxes:
        best = solution.fluxes
    else:
        best = reaction.flux
    return best
Пример #7
0
def metabolite_summary(met,
                       solution=None,
                       threshold=0.01,
                       fva=False,
                       names=False,
                       floatfmt='.3g'):
    """
    Print a summary of the production and consumption fluxes.

    This method requires the model for which this metabolite is a part
    to be solved.

    Parameters
    ----------
    solution : cobra.Solution, optional
        A previously solved model solution to use for generating the
        summary. If none provided (default), the summary method will
        resolve the model. Note that the solution object must match the
        model, i.e., changes to the model such as changed bounds,
        added or removed reactions are not taken into account by this
        method.
    threshold : float, optional
        Threshold below which fluxes are not reported.
    fva : pandas.DataFrame, float or None, optional
        Whether or not to include flux variability analysis in the output.
        If given, fva should either be a previous FVA solution matching
        the model or a float between 0 and 1 representing the
        fraction of the optimum objective to be searched.
    names : bool, optional
        Emit reaction and metabolite names rather than identifiers (default
        False).
    floatfmt : string, optional
        Format string for floats (default '.3g').

    """
    if names:
        emit = attrgetter('name')
    else:
        emit = attrgetter('id')
    if solution is None:
        met.model.slim_optimize(error_value=None)
        solution = get_solution(met.model, reactions=met.reactions)

    rxns = sorted(met.reactions, key=attrgetter("id"))
    rxn_id = list()
    rxn_name = list()
    flux = list()
    reaction = list()
    for rxn in rxns:
        rxn_id.append(rxn.id)
        rxn_name.append(format_long_string(emit(rxn), 10))
        flux.append(solution[rxn.id] * rxn.metabolites[met])
        txt = rxn.build_reaction_string(use_metabolite_names=names)
        reaction.append(format_long_string(txt, 40 if fva is not None else 50))

    flux_summary = pd.DataFrame(
        {
            "id": rxn_name,
            "flux": flux,
            "reaction": reaction
        }, index=rxn_id)

    if fva is not None:
        if hasattr(fva, 'columns'):
            fva_results = fva
        else:
            fva_results = flux_variability_analysis(met.model,
                                                    list(met.reactions),
                                                    fraction_of_optimum=fva)

        flux_summary["maximum"] = zeros(len(rxn_id), dtype=float)
        flux_summary["minimum"] = zeros(len(rxn_id), dtype=float)
        for rxn in rxns:
            fmax = rxn.metabolites[met] * fva_results.at[rxn.id, "maximum"]
            fmin = rxn.metabolites[met] * fva_results.at[rxn.id, "minimum"]
            if abs(fmin) <= abs(fmax):
                flux_summary.at[rxn.id, "fmax"] = fmax
                flux_summary.at[rxn.id, "fmin"] = fmin
            else:
                # Reverse fluxes.
                flux_summary.at[rxn.id, "fmax"] = fmin
                flux_summary.at[rxn.id, "fmin"] = fmax

    assert flux_summary["flux"].sum() < 1E-6, "Error in flux balance"

    flux_summary = _process_flux_dataframe(flux_summary, fva, threshold,
                                           floatfmt)

    flux_summary['percent'] = 0
    total_flux = flux_summary.loc[flux_summary.is_input, "flux"].sum()

    flux_summary.loc[flux_summary.is_input, 'percent'] = \
        flux_summary.loc[flux_summary.is_input, 'flux'] / total_flux
    flux_summary.loc[~flux_summary.is_input, 'percent'] = \
        flux_summary.loc[~flux_summary.is_input, 'flux'] / total_flux

    flux_summary['percent'] = flux_summary.percent.apply(
        lambda x: '{:.0%}'.format(x))

    if fva is not None:
        flux_table = tabulate(
            flux_summary.
            loc[:, ['percent', 'flux', 'fva_fmt', 'id', 'reaction']].values,
            floatfmt=floatfmt,
            headers=['%', 'FLUX', 'RANGE', 'RXN ID', 'REACTION']).split('\n')
    else:
        flux_table = tabulate(
            flux_summary.loc[:, ['percent', 'flux', 'id', 'reaction']].values,
            floatfmt=floatfmt,
            headers=['%', 'FLUX', 'RXN ID', 'REACTION']).split('\n')

    flux_table_head = flux_table[:2]

    met_tag = "{0} ({1})".format(format_long_string(met.name, 45),
                                 format_long_string(met.id, 10))

    head = "PRODUCING REACTIONS -- " + met_tag
    print_(head)
    print_("-" * len(head))
    print_('\n'.join(flux_table_head))
    print_('\n'.join(
        pd.np.array(flux_table[2:])[flux_summary.is_input.values]))

    print_()
    print_("CONSUMING REACTIONS -- " + met_tag)
    print_("-" * len(head))
    print_('\n'.join(flux_table_head))
    print_('\n'.join(
        pd.np.array(flux_table[2:])[~flux_summary.is_input.values]))
Пример #8
0
def metabolite_summary(met, solution=None, threshold=0.01, fva=False,
                       floatfmt='.3g'):
    """Print a summary of the reactions which produce and consume this
    metabolite

    solution : cobra.core.Solution
        A previously solved model solution to use for generating the
        summary. If none provided (default), the summary method will resolve
        the model. Note that the solution object must match the model, i.e.,
        changes to the model such as changed bounds, added or removed
        reactions are not taken into account by this method.

    threshold : float
        a percentage value of the maximum flux below which to ignore reaction
        fluxes

    fva : float (0->1), or None
        Whether or not to include flux variability analysis in the output.
        If given, fva should be a float between 0 and 1, representing the
        fraction of the optimum objective to be searched.

    floatfmt : string
        format method for floats, passed to tabulate. Default is '.3g'.

    """
    if solution is None:
        met.model.slim_optimize(error_value=None)
        solution = get_solution(met.model, reactions=met.reactions)

    rxn_id = list()
    flux = list()
    reaction = list()
    for rxn in met.reactions:
        rxn_id.append(format_long_string(rxn.id, 10))
        flux.append(solution.fluxes[rxn.id] * rxn.metabolites[met])
        reaction.append(format_long_string(rxn.reaction, 40 if fva else 50))

    flux_summary = pd.DataFrame(data={
        "id": rxn_id, "flux": flux, "reaction": reaction})

    if fva:
        fva_results = flux_variability_analysis(
            met.model, met.reactions, fraction_of_optimum=fva)

        flux_summary.index = flux_summary["id"]
        flux_summary["maximum"] = zeros(len(rxn_id))
        flux_summary["minimum"] = zeros(len(rxn_id))
        for rid, rxn in zip(rxn_id, met.reactions):
            imax = rxn.metabolites[met] * fva_results.loc[rxn.id, "maximum"]
            imin = rxn.metabolites[met] * fva_results.loc[rxn.id, "minimum"]
            flux_summary.loc[rid, "fmax"] = (imax if abs(imin) <= abs(imax)
                                             else imin)
            flux_summary.loc[rid, "fmin"] = (imin if abs(imin) <= abs(imax)
                                             else imax)

    assert flux_summary.flux.sum() < 1E-6, "Error in flux balance"

    flux_summary = _process_flux_dataframe(flux_summary, fva, threshold,
                                           floatfmt)

    flux_summary['percent'] = 0
    total_flux = flux_summary[flux_summary.is_input].flux.sum()

    flux_summary.loc[flux_summary.is_input, 'percent'] = \
        flux_summary.loc[flux_summary.is_input, 'flux'] / total_flux
    flux_summary.loc[~flux_summary.is_input, 'percent'] = \
        flux_summary.loc[~flux_summary.is_input, 'flux'] / total_flux

    flux_summary['percent'] = flux_summary.percent.apply(
        lambda x: '{:.0%}'.format(x))

    if fva:
        flux_table = tabulate(
            flux_summary.loc[:, ['percent', 'flux', 'fva_fmt', 'id',
                                 'reaction']].values, floatfmt=floatfmt,
            headers=['%', 'FLUX', 'RANGE', 'RXN ID', 'REACTION']).split('\n')
    else:
        flux_table = tabulate(
            flux_summary.loc[:, ['percent', 'flux', 'id', 'reaction']].values,
            floatfmt=floatfmt, headers=['%', 'FLUX', 'RXN ID', 'REACTION']
        ).split('\n')

    flux_table_head = flux_table[:2]

    met_tag = "{0} ({1})".format(format_long_string(met.name, 45),
                                 format_long_string(met.id, 10))

    head = "PRODUCING REACTIONS -- " + met_tag
    print_(head)
    print_("-" * len(head))
    print_('\n'.join(flux_table_head))
    print_('\n'.join(
        pd.np.array(flux_table[2:])[flux_summary.is_input.values]))

    print_()
    print_("CONSUMING REACTIONS -- " + met_tag)
    print_("-" * len(head))
    print_('\n'.join(flux_table_head))
    print_('\n'.join(
        pd.np.array(flux_table[2:])[~flux_summary.is_input.values]))
Пример #9
0
def loopless_fva_iter(model, reaction, solution=False, zero_cutoff=1e-6):
    """Plugin to get a loopless FVA solution from single FVA iteration.

    Assumes the following about `model` and `reaction`:
    1. the model objective is set to be `reaction`
    2. the model has been optimized and contains the minimum/maximum flux for
       `reaction`
    3. the model contains an auxiliary variable called "fva_old_objective"
       denoting the previous objective

    Parameters
    ----------
    model : cobra.Model
        The model to be used.
    reaction : cobra.Reaction
        The reaction currently minimized/maximized.
    solution : boolean, optional
        Whether to return the entire solution or only the minimum/maximum for
        `reaction`.
    zero_cutoff : positive float, optional
        Cutoff used for loop removal. Fluxes with an absolute value smaller
        than `zero_cutoff` are considered to be zero.

    Returns
    -------
    single float or dict
        Returns the minimized/maximized flux through `reaction` if
        all_fluxes == False (default). Otherwise returns a loopless flux
        solution containing the minimum/maximum flux for `reaction`.
    """
    current = model.objective.value
    sol = get_solution(model)
    objective_dir = model.objective.direction

    # boundary reactions can not be part of cycles
    if reaction.boundary:
        if solution:
            return sol
        else:
            return current

    with model:
        _add_cycle_free(model, sol.fluxes)
        flux = model.slim_optimize()

        # If the previous optimum is maintained in the loopless solution it was
        # loopless and we are done
        if abs(flux - current) < zero_cutoff:
            if solution:
                return sol
            return current

        # If previous optimum was not in the loopless solution create a new
        # almost loopless solution containing only loops including the current
        # reaction. Than remove all of those loops.
        ll_sol = get_solution(model).fluxes
        bounds = reaction.bounds
        reaction.bounds = (current, current)
        model.slim_optimize()
        almost_ll_sol = get_solution(model).fluxes
        reaction.bounds = bounds
        # find the reactions with loops using the current reaction and remove
        # the loops
        for rxn in model.reactions:
            rid = rxn.id
            if ((abs(ll_sol[rid]) < zero_cutoff) and
                    (abs(almost_ll_sol[rid]) > zero_cutoff)):
                rxn.bounds = max(0, rxn.lower_bound), min(0, rxn.upper_bound)

        if solution:
            best = model.optimize()
        else:
            model.slim_optimize()
            best = reaction.flux
    model.objective.direction = objective_dir
    return best
    def _generate(self):
        """
        Returns
        -------
        flux_summary: pandas.DataFrame
            The DataFrame of flux summary data.

        """
        if self.names:
            emit = attrgetter('name')
        else:
            emit = attrgetter('id')

        if self.solution is None:
            self.metabolite.model.slim_optimize(error_value=None)
            self.solution = get_solution(self.metabolite.model,
                                         reactions=self.metabolite.reactions)

        rxns = sorted(self.metabolite.reactions, key=attrgetter('id'))

        data = [
            (emit(rxn),
             self.solution[rxn.id] * rxn.metabolites[self.metabolite],
             rxn.build_reaction_string(use_metabolite_names=self.names), rxn)
            for rxn in rxns
        ]

        flux_summary = pd.DataFrame.from_records(
            data=data,
            index=[rxn.id for rxn in rxns],
            columns=['id', 'flux', 'reaction_string', 'reaction'])

        assert flux_summary['flux'].sum() < self.metabolite.model.tolerance, \
            'Error in flux balance'

        if self.fva is not None:
            if hasattr(self.fva, 'columns'):
                fva_results = self.fva
            else:
                fva_results = flux_variability_analysis(
                    self.metabolite.model,
                    list(self.metabolite.reactions),
                    fraction_of_optimum=self.fva)

            flux_summary = pd.concat([flux_summary, fva_results],
                                     axis=1,
                                     sort=False)
            flux_summary.rename(columns={
                'maximum': 'fmax',
                'minimum': 'fmin'
            },
                                inplace=True)

            def set_min_and_max(row):
                """Scale and set proper min and max values for flux."""
                fmax = row.reaction.metabolites[self.metabolite] * row.fmax
                fmin = row.reaction.metabolites[self.metabolite] * row.fmin

                if abs(fmin) <= abs(fmax):
                    row.fmax = fmax
                    row.fmin = fmin
                else:
                    # Reverse fluxes
                    row.fmax = fmin
                    row.fmin = fmax

                return row

            flux_summary = flux_summary.apply(set_min_and_max, axis=1)

        flux_summary = self._process_flux_dataframe(flux_summary)

        total_flux = flux_summary.loc[flux_summary.is_input, 'flux'].sum()

        # Calculate flux percentage
        flux_summary['percent'] = (flux_summary['flux'] / total_flux) * 100

        return flux_summary
Пример #11
0
def model_summary(model, solution=None, threshold=0.01, fva=None,
                  floatfmt='.3g'):
    """Print a summary of the input and output fluxes of the model.

    solution : cobra.core.Solution
        A previously solved model solution to use for generating the
        summary. If none provided (default), the summary method will resolve
        the model. Note that the solution object must match the model, i.e.,
        changes to the model such as changed bounds, added or removed
        reactions are not taken into account by this method.

    threshold : float
        a percentage value of the maximum flux below which to ignore reaction
        fluxes

    fva : int or None
        Whether or not to calculate and report flux variability in the
        output summary

    floatfmt : string
        format method for floats, passed to tabulate. Default is '.3g'.

    """
    objective_reactions = linear_reaction_coefficients(model)
    boundary_reactions = model.exchanges
    summary_rxns = set(objective_reactions.keys()).union(boundary_reactions)

    if solution is None:
        model.slim_optimize(error_value=None)
        solution = get_solution(model, reactions=summary_rxns)

    # Create a dataframe of objective fluxes
    obj_fluxes = pd.DataFrame({key: solution.fluxes[key.id] * value for key,
                               value in iteritems(objective_reactions)},
                              index=['flux']).T
    obj_fluxes['id'] = obj_fluxes.apply(
        lambda x: format_long_string(x.name.id, 15), 1)

    # Build a dictionary of metabolite production from the boundary reactions
    metabolite_fluxes = {}
    for rxn in boundary_reactions:
        for met, stoich in iteritems(rxn.metabolites):
            metabolite_fluxes[met] = {
                'id': format_long_string(met.id, 15),
                'flux': stoich * solution.fluxes[rxn.id]}

    # Calculate FVA results if requested
    if fva:
        fva_results = flux_variability_analysis(
            model, reaction_list=boundary_reactions, fraction_of_optimum=fva)

        for rxn in boundary_reactions:
            for met, stoich in iteritems(rxn.metabolites):
                imin = stoich * fva_results.loc[rxn.id]['minimum']
                imax = stoich * fva_results.loc[rxn.id]['maximum']
                # Correct 'max' and 'min' for negative values
                metabolite_fluxes[met].update({
                    'fmin': imin if abs(imin) <= abs(imax) else imax,
                    'fmax': imax if abs(imin) <= abs(imax) else imin,
                })

    # Generate a dataframe of boundary fluxes
    metabolite_fluxes = pd.DataFrame(metabolite_fluxes).T
    metabolite_fluxes = _process_flux_dataframe(
        metabolite_fluxes, fva, threshold, floatfmt)

    # Begin building string output table
    def get_str_table(species_df, fva=False):
        """Formats a string table for each column"""

        if not fva:
            return tabulate(species_df.loc[:, ['id', 'flux']].values,
                            floatfmt=floatfmt, tablefmt='plain').split('\n')

        else:
            return tabulate(
                species_df.loc[:, ['id', 'flux', 'fva_fmt']].values,
                floatfmt=floatfmt, tablefmt='simple',
                headers=['id', 'Flux', 'Range']).split('\n')

    in_table = get_str_table(
        metabolite_fluxes[metabolite_fluxes.is_input], fva=fva)
    out_table = get_str_table(
        metabolite_fluxes[~metabolite_fluxes.is_input], fva=fva)
    obj_table = get_str_table(obj_fluxes, fva=False)

    # Print nested output table
    print_(tabulate(
        [entries for entries in zip_longest(in_table, out_table, obj_table)],
        headers=['IN FLUXES', 'OUT FLUXES', 'OBJECTIVES'], tablefmt='simple'))
Пример #12
0
def model_summary(model, solution=None, threshold=0.01, fva=None, names=False,
                  floatfmt='.3g'):
    """
    Print a summary of the input and output fluxes of the model.

    Parameters
    ----------
    solution: cobra.Solution, optional
        A previously solved model solution to use for generating the
        summary. If none provided (default), the summary method will
        resolve the model. Note that the solution object must match the
        model, i.e., changes to the model such as changed bounds,
        added or removed reactions are not taken into account by this
        method.
    threshold : float, optional
        Threshold below which fluxes are not reported.
    fva : pandas.DataFrame, float or None, optional
        Whether or not to include flux variability analysis in the output.
        If given, fva should either be a previous FVA solution matching
        the model or a float between 0 and 1 representing the
        fraction of the optimum objective to be searched.
    names : bool, optional
        Emit reaction and metabolite names rather than identifiers (default
        False).
    floatfmt : string, optional
        Format string for floats (default '.3g').

    """
    if names:
        emit = attrgetter('name')
    else:
        emit = attrgetter('id')
    objective_reactions = linear_reaction_coefficients(model)
    boundary_reactions = model.exchanges
    summary_rxns = set(objective_reactions.keys()).union(boundary_reactions)

    if solution is None:
        model.slim_optimize(error_value=None)
        solution = get_solution(model, reactions=summary_rxns)

    # Create a dataframe of objective fluxes
    obj_fluxes = pd.DataFrame({key: solution[key.id] * value for key,
                               value in iteritems(objective_reactions)},
                              index=['flux']).T
    obj_fluxes['id'] = obj_fluxes.apply(
        lambda x: format_long_string(x.name.id, 15), 1)

    # Build a dictionary of metabolite production from the boundary reactions
    metabolites = {m for r in boundary_reactions for m in r.metabolites}
    index = sorted(metabolites, key=attrgetter('id'))
    metabolite_fluxes = pd.DataFrame({
        'id': [format_long_string(emit(m), 15) for m in index],
        'flux': zeros(len(index), dtype=float)
    }, index=[m.id for m in index])
    for rxn in boundary_reactions:
        for met, stoich in iteritems(rxn.metabolites):
            metabolite_fluxes.at[met.id, 'flux'] += stoich * solution[rxn.id]

    # Calculate FVA results if requested
    if fva is not None:
        if len(index) != len(boundary_reactions):
            LOGGER.warning(
                "There exists more than one boundary reaction per metabolite. "
                "Please be careful when evaluating flux ranges.")
        metabolite_fluxes['fmin'] = zeros(len(index), dtype=float)
        metabolite_fluxes['fmax'] = zeros(len(index), dtype=float)
        if hasattr(fva, 'columns'):
            fva_results = fva
        else:
            fva_results = flux_variability_analysis(
                model, reaction_list=boundary_reactions,
                fraction_of_optimum=fva)

        for rxn in boundary_reactions:
            for met, stoich in iteritems(rxn.metabolites):
                fmin = stoich * fva_results.at[rxn.id, 'minimum']
                fmax = stoich * fva_results.at[rxn.id, 'maximum']
                # Correct 'max' and 'min' for negative values
                if abs(fmin) <= abs(fmax):
                    metabolite_fluxes.at[met.id, 'fmin'] += fmin
                    metabolite_fluxes.at[met.id, 'fmax'] += fmax
                else:
                    metabolite_fluxes.at[met.id, 'fmin'] += fmax
                    metabolite_fluxes.at[met.id, 'fmax'] += fmin

    # Generate a dataframe of boundary fluxes
    metabolite_fluxes = _process_flux_dataframe(
        metabolite_fluxes, fva, threshold, floatfmt)

    # Begin building string output table
    def get_str_table(species_df, fva=False):
        """Formats a string table for each column"""
        if fva:
            return tabulate(
                species_df.loc[:, ['id', 'flux', 'fva_fmt']].values,
                floatfmt=floatfmt, tablefmt='simple',
                headers=['id', 'Flux', 'Range']).split('\n')
        else:
            return tabulate(species_df.loc[:, ['id', 'flux']].values,
                            floatfmt=floatfmt, tablefmt='plain').split('\n')

    in_table = get_str_table(
        metabolite_fluxes[metabolite_fluxes['is_input']], fva=fva is not None)
    out_table = get_str_table(
        metabolite_fluxes[~metabolite_fluxes['is_input']], fva=fva is not None)
    obj_table = get_str_table(obj_fluxes, fva=False)

    # Print nested output table
    print_(tabulate(
        [entries for entries in zip_longest(in_table, out_table, obj_table)],
        headers=['IN FLUXES', 'OUT FLUXES', 'OBJECTIVES'], tablefmt='simple'))
Пример #13
0
def _cycle_free_fva(model, reactions=None, sloppy=True, sloppy_bound=666):
    """Cycle free flux-variability analysis. (http://cran.r-project.org/web/packages/sybilcycleFreeFlux/index.html)

    Parameters
    ----------
    model : cobra.Model
    reactions : list
        List of reactions whose flux-ranges should be determined.
    sloppy : boolean, optional
        If true, only fluxes v with abs(v) > sloppy_bound are checked to be futile cycles (defaults to True).
    sloppy_bound : int, optional
        The threshold bound used by sloppy (defaults to the number of the beast).
    """
    cycle_count = 0
    if reactions is None:
        reactions = model.reactions
    else:
        reactions = model.reactions.get_by_any(reactions)
    fva_sol = OrderedDict()
    for reaction in reactions:
        fva_sol[reaction.id] = dict()
        model.objective = reaction
        model.objective.direction = 'min'
        model.solver.optimize()
        if model.solver.status == UNBOUNDED:
            fva_sol[reaction.id]['lower_bound'] = -numpy.inf
            continue
        elif model.solver.status != OPTIMAL:
            fva_sol[reaction.id]['lower_bound'] = 0
            continue
        bound = model.objective.value
        if sloppy and abs(bound) < sloppy_bound:
            fva_sol[reaction.id]['lower_bound'] = bound
        else:
            logger.debug('Determine if {} with bound {} is a cycle'.format(
                reaction.id, bound))
            solution = get_solution(model)
            v0_fluxes = solution.fluxes
            v1_cycle_free_fluxes = remove_infeasible_cycles(model, v0_fluxes)
            if abs(v1_cycle_free_fluxes[reaction.id] - bound) < 10**-6:
                fva_sol[reaction.id]['lower_bound'] = bound
            else:
                logger.debug('Cycle detected: {}'.format(reaction.id))
                cycle_count += 1
                v2_one_cycle_fluxes = remove_infeasible_cycles(
                    model, v0_fluxes, fix=[reaction.id])
                with model:
                    for key, v1_flux in six.iteritems(v1_cycle_free_fluxes):
                        if round(v1_flux, config.ndecimals) == 0 and round(
                                v2_one_cycle_fluxes[key],
                                config.ndecimals) != 0:
                            knockout_reaction = model.reactions.get_by_id(key)
                            knockout_reaction.knock_out()
                    model.objective.direction = 'min'
                    model.solver.optimize()
                    if model.solver.status == OPTIMAL:
                        fva_sol[
                            reaction.id]['lower_bound'] = model.objective.value
                    elif model.solver.status == UNBOUNDED:
                        fva_sol[reaction.id]['lower_bound'] = -numpy.inf
                    else:
                        fva_sol[reaction.id]['lower_bound'] = 0

    for reaction in reactions:
        model.objective = reaction
        model.objective.direction = 'max'
        model.solver.optimize()
        if model.solver.status == UNBOUNDED:
            fva_sol[reaction.id]['upper_bound'] = numpy.inf
            continue
        elif model.solver.status != OPTIMAL:
            fva_sol[reaction.id]['upper_bound'] = 0
            continue
        bound = model.objective.value
        if sloppy and abs(bound) < sloppy_bound:
            fva_sol[reaction.id]['upper_bound'] = bound
        else:
            logger.debug('Determine if {} with bound {} is a cycle'.format(
                reaction.id, bound))
            solution = get_solution(model)
            v0_fluxes = solution.fluxes
            v1_cycle_free_fluxes = remove_infeasible_cycles(model, v0_fluxes)
            if abs(v1_cycle_free_fluxes[reaction.id] - bound) < 1e-6:
                fva_sol[reaction.id]['upper_bound'] = v0_fluxes[reaction.id]
            else:
                logger.debug('Cycle detected: {}'.format(reaction.id))
                cycle_count += 1
                v2_one_cycle_fluxes = remove_infeasible_cycles(
                    model, v0_fluxes, fix=[reaction.id])
                with model:
                    for key, v1_flux in six.iteritems(v1_cycle_free_fluxes):
                        if round(v1_flux, config.ndecimals) == 0 and round(
                                v2_one_cycle_fluxes[key],
                                config.ndecimals) != 0:
                            knockout_reaction = model.reactions.get_by_id(key)
                            knockout_reaction.knock_out()
                    model.objective.direction = 'max'
                    model.solver.optimize()
                    if model.solver.status == OPTIMAL:
                        fva_sol[
                            reaction.id]['upper_bound'] = model.objective.value
                    elif model.solver.status == UNBOUNDED:
                        fva_sol[reaction.id]['upper_bound'] = numpy.inf
                    else:
                        fva_sol[reaction.id]['upper_bound'] = 0

    df = pandas.DataFrame.from_dict(fva_sol, orient='index')
    lb_higher_ub = df[df.lower_bound > df.upper_bound]
    # Assert that these cases really only numerical artifacts
    assert ((lb_higher_ub.lower_bound - lb_higher_ub.upper_bound) < 1e-6).all()
    df.lower_bound[lb_higher_ub.index] = df.upper_bound[lb_higher_ub.index]

    return df
Пример #14
0
def metabolite_summary(met, solution=None, threshold=0.01, fva=False,
                       names=False, floatfmt='.3g'):
    """
    Print a summary of the production and consumption fluxes.

    This method requires the model for which this metabolite is a part
    to be solved.

    Parameters
    ----------
    solution : cobra.Solution, optional
        A previously solved model solution to use for generating the
        summary. If none provided (default), the summary method will
        resolve the model. Note that the solution object must match the
        model, i.e., changes to the model such as changed bounds,
        added or removed reactions are not taken into account by this
        method.
    threshold : float, optional
        Threshold below which fluxes are not reported.
    fva : pandas.DataFrame, float or None, optional
        Whether or not to include flux variability analysis in the output.
        If given, fva should either be a previous FVA solution matching
        the model or a float between 0 and 1 representing the
        fraction of the optimum objective to be searched.
    names : bool, optional
        Emit reaction and metabolite names rather than identifiers (default
        False).
    floatfmt : string, optional
        Format string for floats (default '.3g').

    """
    if names:
        emit = attrgetter('name')
    else:
        emit = attrgetter('id')
    if solution is None:
        met.model.slim_optimize(error_value=None)
        solution = get_solution(met.model, reactions=met.reactions)

    rxns = sorted(met.reactions, key=attrgetter("id"))
    rxn_id = list()
    rxn_name = list()
    flux = list()
    reaction = list()
    for rxn in rxns:
        rxn_id.append(rxn.id)
        rxn_name.append(format_long_string(emit(rxn), 10))
        flux.append(solution[rxn.id] * rxn.metabolites[met])
        txt = rxn.build_reaction_string(use_metabolite_names=names)
        reaction.append(format_long_string(txt, 40 if fva is not None else 50))

    flux_summary = pd.DataFrame({
        "id": rxn_name,
        "flux": flux,
        "reaction": reaction
    }, index=rxn_id)

    if fva is not None:
        if hasattr(fva, 'columns'):
            fva_results = fva
        else:
            fva_results = flux_variability_analysis(
                met.model, list(met.reactions), fraction_of_optimum=fva)

        flux_summary["maximum"] = zeros(len(rxn_id), dtype=float)
        flux_summary["minimum"] = zeros(len(rxn_id), dtype=float)
        for rxn in rxns:
            fmax = rxn.metabolites[met] * fva_results.at[rxn.id, "maximum"]
            fmin = rxn.metabolites[met] * fva_results.at[rxn.id, "minimum"]
            if abs(fmin) <= abs(fmax):
                flux_summary.at[rxn.id, "fmax"] = fmax
                flux_summary.at[rxn.id, "fmin"] = fmin
            else:
                # Reverse fluxes.
                flux_summary.at[rxn.id, "fmax"] = fmin
                flux_summary.at[rxn.id, "fmin"] = fmax

    assert flux_summary["flux"].sum() < 1E-6, "Error in flux balance"

    flux_summary = _process_flux_dataframe(flux_summary, fva, threshold,
                                           floatfmt)

    flux_summary['percent'] = 0
    total_flux = flux_summary.loc[flux_summary.is_input, "flux"].sum()

    flux_summary.loc[flux_summary.is_input, 'percent'] = \
        flux_summary.loc[flux_summary.is_input, 'flux'] / total_flux
    flux_summary.loc[~flux_summary.is_input, 'percent'] = \
        flux_summary.loc[~flux_summary.is_input, 'flux'] / total_flux

    flux_summary['percent'] = flux_summary.percent.apply(
        lambda x: '{:.0%}'.format(x))

    if fva is not None:
        flux_table = tabulate(
            flux_summary.loc[:, ['percent', 'flux', 'fva_fmt', 'id',
                                 'reaction']].values, floatfmt=floatfmt,
            headers=['%', 'FLUX', 'RANGE', 'RXN ID', 'REACTION']).split('\n')
    else:
        flux_table = tabulate(
            flux_summary.loc[:, ['percent', 'flux', 'id', 'reaction']].values,
            floatfmt=floatfmt, headers=['%', 'FLUX', 'RXN ID', 'REACTION']
        ).split('\n')

    flux_table_head = flux_table[:2]

    met_tag = "{0} ({1})".format(format_long_string(met.name, 45),
                                 format_long_string(met.id, 10))

    head = "PRODUCING REACTIONS -- " + met_tag
    print_(head)
    print_("-" * len(head))
    print_('\n'.join(flux_table_head))
    print_('\n'.join(
        pd.np.array(flux_table[2:])[flux_summary.is_input.values]))

    print_()
    print_("CONSUMING REACTIONS -- " + met_tag)
    print_("-" * len(head))
    print_('\n'.join(flux_table_head))
    print_('\n'.join(
        pd.np.array(flux_table[2:])[~flux_summary.is_input.values]))
Пример #15
0
def model_summary(model,
                  solution=None,
                  threshold=0.01,
                  fva=None,
                  names=False,
                  floatfmt='.3g'):
    """
    Print a summary of the input and output fluxes of the model.

    Parameters
    ----------
    solution: cobra.Solution, optional
        A previously solved model solution to use for generating the
        summary. If none provided (default), the summary method will
        resolve the model. Note that the solution object must match the
        model, i.e., changes to the model such as changed bounds,
        added or removed reactions are not taken into account by this
        method.
    threshold : float, optional
        Threshold below which fluxes are not reported.
    fva : pandas.DataFrame, float or None, optional
        Whether or not to include flux variability analysis in the output.
        If given, fva should either be a previous FVA solution matching
        the model or a float between 0 and 1 representing the
        fraction of the optimum objective to be searched.
    names : bool, optional
        Emit reaction and metabolite names rather than identifiers (default
        False).
    floatfmt : string, optional
        Format string for floats (default '.3g').

    """
    if names:
        emit = attrgetter('name')
    else:
        emit = attrgetter('id')
    objective_reactions = linear_reaction_coefficients(model)
    boundary_reactions = model.exchanges
    summary_rxns = set(objective_reactions.keys()).union(boundary_reactions)

    if solution is None:
        model.slim_optimize(error_value=None)
        solution = get_solution(model, reactions=summary_rxns)

    # Create a dataframe of objective fluxes
    obj_fluxes = pd.DataFrame(
        {
            key: solution[key.id] * value
            for key, value in iteritems(objective_reactions)
        },
        index=['flux']).T
    obj_fluxes['id'] = obj_fluxes.apply(
        lambda x: format_long_string(x.name.id, 15), 1)

    # Build a dictionary of metabolite production from the boundary reactions
    metabolites = {m for r in boundary_reactions for m in r.metabolites}
    index = sorted(metabolites, key=attrgetter('id'))
    metabolite_fluxes = pd.DataFrame(
        {
            'id': [format_long_string(emit(m), 15) for m in index],
            'flux': zeros(len(index), dtype=float)
        },
        index=[m.id for m in index])
    for rxn in boundary_reactions:
        for met, stoich in iteritems(rxn.metabolites):
            metabolite_fluxes.at[met.id, 'flux'] += stoich * solution[rxn.id]

    # Calculate FVA results if requested
    if fva is not None:
        if len(index) != len(boundary_reactions):
            LOGGER.warning(
                "There exists more than one boundary reaction per metabolite. "
                "Please be careful when evaluating flux ranges.")
        metabolite_fluxes['fmin'] = zeros(len(index), dtype=float)
        metabolite_fluxes['fmax'] = zeros(len(index), dtype=float)
        if hasattr(fva, 'columns'):
            fva_results = fva
        else:
            fva_results = flux_variability_analysis(
                model,
                reaction_list=boundary_reactions,
                fraction_of_optimum=fva)

        for rxn in boundary_reactions:
            for met, stoich in iteritems(rxn.metabolites):
                fmin = stoich * fva_results.at[rxn.id, 'minimum']
                fmax = stoich * fva_results.at[rxn.id, 'maximum']
                # Correct 'max' and 'min' for negative values
                if abs(fmin) <= abs(fmax):
                    metabolite_fluxes.at[met.id, 'fmin'] += fmin
                    metabolite_fluxes.at[met.id, 'fmax'] += fmax
                else:
                    metabolite_fluxes.at[met.id, 'fmin'] += fmax
                    metabolite_fluxes.at[met.id, 'fmax'] += fmin

    # Generate a dataframe of boundary fluxes
    metabolite_fluxes = _process_flux_dataframe(metabolite_fluxes, fva,
                                                threshold, floatfmt)

    # Begin building string output table
    def get_str_table(species_df, fva=False):
        """Formats a string table for each column"""
        if fva:
            return tabulate(species_df.loc[:,
                                           ['id', 'flux', 'fva_fmt']].values,
                            floatfmt=floatfmt,
                            tablefmt='simple',
                            headers=['id', 'Flux', 'Range']).split('\n')
        else:
            return tabulate(species_df.loc[:, ['id', 'flux']].values,
                            floatfmt=floatfmt,
                            tablefmt='plain').split('\n')

    in_table = get_str_table(metabolite_fluxes[metabolite_fluxes['is_input']],
                             fva=fva is not None)
    out_table = get_str_table(
        metabolite_fluxes[~metabolite_fluxes['is_input']], fva=fva is not None)
    obj_table = get_str_table(obj_fluxes, fva=False)

    # Print nested output table
    print_(
        tabulate([
            entries for entries in zip_longest(in_table, out_table, obj_table)
        ],
                 headers=['IN FLUXES', 'OUT FLUXES', 'OBJECTIVES'],
                 tablefmt='simple'))
Пример #16
0
def loopless_fva_iter(model, reaction, solution=False, zero_cutoff=None):
    """Plugin to get a loopless FVA solution from single FVA iteration.

    Assumes the following about `model` and `reaction`:
    1. the model objective is set to be `reaction`
    2. the model has been optimized and contains the minimum/maximum flux for
       `reaction`
    3. the model contains an auxiliary variable called "fva_old_objective"
       denoting the previous objective

    Parameters
    ----------
    model : cobra.Model
        The model to be used.
    reaction : cobra.Reaction
        The reaction currently minimized/maximized.
    solution : boolean, optional
        Whether to return the entire solution or only the minimum/maximum for
        `reaction`.
    zero_cutoff : positive float, optional
        Cutoff used for loop removal. Fluxes with an absolute value smaller
        than `zero_cutoff` are considered to be zero (default model.tolerance).

    Returns
    -------
    single float or dict
        Returns the minimized/maximized flux through `reaction` if
        all_fluxes == False (default). Otherwise returns a loopless flux
        solution containing the minimum/maximum flux for `reaction`.
    """
    zero_cutoff = normalize_cutoff(model, zero_cutoff)

    current = model.objective.value
    sol = get_solution(model)
    objective_dir = model.objective.direction

    # boundary reactions can not be part of cycles
    if reaction.boundary:
        if solution:
            return sol
        else:
            return current

    with model:
        _add_cycle_free(model, sol.fluxes)
        model.slim_optimize()

        # If the previous optimum is maintained in the loopless solution it was
        # loopless and we are done
        if abs(reaction.flux - current) < zero_cutoff:
            if solution:
                return sol
            return current

        # If previous optimum was not in the loopless solution create a new
        # almost loopless solution containing only loops including the current
        # reaction. Than remove all of those loops.
        ll_sol = get_solution(model).fluxes
        reaction.bounds = (current, current)
        model.slim_optimize()
        almost_ll_sol = get_solution(model).fluxes

    with model:
        # find the reactions with loops using the current reaction and remove
        # the loops
        for rxn in model.reactions:
            rid = rxn.id
            if ((abs(ll_sol[rid]) < zero_cutoff) and
                    (abs(almost_ll_sol[rid]) > zero_cutoff)):
                rxn.bounds = max(0, rxn.lower_bound), min(0, rxn.upper_bound)

        if solution:
            best = model.optimize()
        else:
            model.slim_optimize()
            best = reaction.flux
    model.objective.direction = objective_dir
    return best
Пример #17
0
def geometric_fba(model, epsilon=1E-06, max_tries=200):
    """Perform geometric FBA to obtain a unique, centered flux distribution.

    Geometric FBA [1]_ formulates the problem as a polyhedron and
    then solves it by bounding the convex hull of the polyhedron.
    The bounding forms a box around the convex hull which reduces
    with every iteration and extracts a unique solution in this way.

    Parameters
    ----------
    model: cobra.Model
        The model to perform geometric FBA on.
    epsilon: float, optional
        The convergence tolerance of the model (default 1E-06).
    max_tries: int, optional
        Maximum number of iterations (default 200).

    Returns
    -------
    cobra.Solution
        The solution object containing all the constraints required
        for geometric FBA.

    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.
    """

    with model:
        # iteration parameters
        delta = 1.0  # initialize at 1.0 to enter while loop
        count = 2  # iteration #1 happens out of the loop

        # vars and consts storage variables
        consts = []
        obj_vars = []
        updating_vars_cons = []

        # first iteration
        prob = model.problem
        add_pfba(model)  # minimizes the solution space to convex hull
        model.optimize()
        fva_sol = flux_variability_analysis(model)
        mean_flux = (fva_sol["maximum"] + fva_sol["minimum"]).abs() / 2

        # set gFBA constraints
        for rxn in model.reactions:
            var = prob.Variable("geometric_fba_" + rxn.id,
                                lb=0,
                                ub=mean_flux[rxn.id])
            upper_const = prob.Constraint(rxn.flux_expression - var,
                                          ub=mean_flux[rxn.id],
                                          name="geometric_fba_upper_const_" +
                                          rxn.id)
            lower_const = prob.Constraint(rxn.flux_expression + var,
                                          lb=fva_sol.at[rxn.id, "minimum"],
                                          name="geometric_fba_lower_const_" +
                                          rxn.id)
            updating_vars_cons.append((rxn.id, var, upper_const, lower_const))
            consts.extend([var, upper_const, lower_const])
            obj_vars.append(var)
        model.add_cons_vars(consts)

        # minimize distance between flux and centre
        model.objective = prob.Objective(Zero, sloppy=True, direction="min")
        model.objective.set_linear_coefficients({v: 1.0 for v in obj_vars})
        model.optimize()

        # further iterations
        while delta > epsilon and count <= max_tries:
            fva_sol = flux_variability_analysis(model)
            mean_flux = (fva_sol["maximum"] + fva_sol["minimum"]).abs() / 2

            for rxn_id, var, u_c, l_c in updating_vars_cons:
                var.ub = mean_flux[rxn_id]
                u_c.ub = mean_flux[rxn_id]
                l_c.lb = fva_sol.at[rxn_id, "minimum"]
            model.optimize()
            delta = (fva_sol["maximum"] - fva_sol["minimum"]).max()

            count += 1
            if count == max_tries:
                raise RuntimeError(
                    "The iterations have exceeded the maximum value of {}. "
                    "This is probably due to the increased complexity of the "
                    "model and can lead to inaccurate results. Please set a "
                    "different convergence tolerance and/or increase the "
                    "maximum iterations".format(max_tries))

    return get_solution(model)
Пример #18
0
def geometric_fba(model, epsilon=1E-06, max_tries=200):
    """Perform geometric FBA to obtain a unique, centered flux distribution.

    Geometric FBA [1]_ formulates the problem as a polyhedron and
    then solves it by bounding the convex hull of the polyhedron.
    The bounding forms a box around the convex hull which reduces
    with every iteration and extracts a unique solution in this way.

    Parameters
    ----------
    model: cobra.Model
        The model to perform geometric FBA on.
    epsilon: float, optional
        The convergence tolerance of the model (default 1E-06).
    max_tries: int, optional
        Maximum number of iterations (default 200).

    Returns
    -------
    cobra.Solution
        The solution object containing all the constraints required
        for geometric FBA.

    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.
    """

    with model:
        # iteration parameters
        delta = 1.0  # initialize at 1.0 to enter while loop
        count = 2  # iteration #1 happens out of the loop

        # vars and consts storage variables
        consts = []
        obj_vars = []
        updating_vars_cons = []

        # first iteration
        prob = model.problem
        add_pfba(model)  # minimizes the solution space to convex hull
        model.optimize()
        fva_sol = flux_variability_analysis(model)
        mean_flux = (fva_sol["maximum"] + fva_sol["minimum"]).abs() / 2

        # set gFBA constraints
        for rxn in model.reactions:
            var = prob.Variable("geometric_fba_" + rxn.id,
                                lb=0,
                                ub=mean_flux[rxn.id])
            upper_const = prob.Constraint(rxn.flux_expression - var,
                                          ub=mean_flux[rxn.id],
                                          name="geometric_fba_upper_const_" +
                                          rxn.id)
            lower_const = prob.Constraint(rxn.flux_expression + var,
                                          lb=fva_sol.at[rxn.id, "minimum"],
                                          name="geometric_fba_lower_const_" +
                                          rxn.id)
            updating_vars_cons.append((rxn.id, var, upper_const, lower_const))
            consts.extend([var, upper_const, lower_const])
            obj_vars.append(var)
        model.add_cons_vars(consts)

        # minimize distance between flux and centre
        model.objective = prob.Objective(Zero, sloppy=True, direction="min")
        model.objective.set_linear_coefficients({v: 1.0 for v in obj_vars})
        model.optimize()

        # further iterations
        while delta > epsilon and count <= max_tries:
            fva_sol = flux_variability_analysis(model)
            mean_flux = (fva_sol["maximum"] + fva_sol["minimum"]).abs() / 2

            for rxn_id, var, u_c, l_c in updating_vars_cons:
                var.ub = mean_flux[rxn_id]
                u_c.ub = mean_flux[rxn_id]
                l_c.lb = fva_sol.at[rxn_id, "minimum"]
            model.optimize()
            delta = (fva_sol["maximum"] - fva_sol["minimum"]).max()

            count += 1
            if count == max_tries:
                raise RuntimeError(
                    "The iterations have exceeded the maximum value of {}. "
                    "This is probably due to the increased complexity of the "
                    "model and can lead to inaccurate results. Please set a "
                    "different convergence tolerance and/or increase the "
                    "maximum iterations".format(max_tries)
                )

    return get_solution(model)
Пример #19
0
def find_blocked_reactions(model,
                           reaction_list=None,
                           solver=None,
                           zero_cutoff=1e-9,
                           open_exchanges=False,
                           **solver_args):
    """Finds reactions that cannot carry a flux with the current
    exchange reaction settings for a cobra model, using flux variability
    analysis.

    Parameters
    ----------
    model : cobra.Model
        The model to analyze
    reaction_list : list
        List of reactions to consider, use all if left missing
    solver : string
        The name of the solver to use
    zero_cutoff : float
        Flux value which is considered to effectively be zero.
    open_exchanges : bool
        If true, set bounds on exchange reactions to very high values to
        avoid that being the bottle-neck.
    **solver_args :
        Additional arguments to the solver. Ignored for optlang based solvers.

    Returns
    -------
    list
        List with the blocked reactions
    """
    legacy, solver_interface = sutil.choose_solver(model, solver)
    with model:
        if open_exchanges:
            for reaction in model.exchanges:
                reaction.bounds = (min(reaction.lower_bound,
                                       -1000), max(reaction.upper_bound, 1000))
        if reaction_list is None:
            reaction_list = model.reactions
        # limit to reactions which are already 0. If the reactions already
        # carry flux in this solution, then they can not be blocked.
        if legacy:
            solution = solver_interface.solve(model, **solver_args)
            reaction_list = [
                i for i in reaction_list
                if abs(solution.x_dict[i.id]) < zero_cutoff
            ]
        else:
            model.solver = solver_interface
            model.solver.optimize()
            solution = get_solution(model, reactions=reaction_list)
            reaction_list = [
                rxn for rxn in reaction_list
                if abs(solution.fluxes[rxn.id]) < zero_cutoff
            ]
        # run fva to find reactions where both max and min are 0
        flux_span = flux_variability_analysis(model,
                                              fraction_of_optimum=0.,
                                              reaction_list=reaction_list,
                                              solver=solver,
                                              **solver_args)
        return [
            rxn_id for rxn_id, min_max in flux_span.iterrows()
            if max(abs(min_max)) < zero_cutoff
        ]
Пример #20
0
    def _generate(self):
        """
        Returns
        -------
        flux_summary: pandas.DataFrame
            The DataFrame of flux summary data.

        """
        if self.names:
            emit = attrgetter('name')
        else:
            emit = attrgetter('id')

        obj_rxns = linear_reaction_coefficients(self.model)
        boundary_rxns = self.model.exchanges
        summary_rxns = set(obj_rxns.keys()).union(boundary_rxns)

        if self.solution is None:
            self.model.slim_optimize(error_value=None)
            self.solution = get_solution(self.model, reactions=summary_rxns)

        # the order needs to be maintained
        ord_obj_rxns = OrderedDict(obj_rxns)

        obj_data = [(emit(rxn), self.solution[rxn.id] * stoich, np.nan, rxn,
                     np.nan, 1.0) for rxn, stoich in iteritems(ord_obj_rxns)]

        # create a DataFrame of objective reactions
        obj_df = pd.DataFrame.from_records(
            data=obj_data,
            index=[rxn.id for rxn in iterkeys(ord_obj_rxns)],
            columns=[
                'id', 'flux', 'metabolite', 'reaction', 'is_input',
                'is_obj_rxn'
            ])

        # create a collection of metabolites from the boundary reactions
        mets = OrderedDict(
            (met, rxn) for rxn in boundary_rxns
            for met in sorted(rxn.metabolites, key=attrgetter('id')))
        index = [met.id for met in iterkeys(mets)]

        met_data = [(emit(met), self.solution[rxn.id] * rxn.metabolites[met],
                     met, rxn) for met, rxn in iteritems(mets)]

        # create a DataFrame of metabolites
        flux_summary = pd.DataFrame.from_records(
            data=met_data,
            index=index,
            columns=['id', 'flux', 'metabolite', 'reaction'])

        # Calculate FVA results if requested
        if self.fva is not None:
            if len(index) != len(boundary_rxns):
                logger.warning(
                    'There exists more than one boundary reaction per '
                    'metabolite. Please be careful when evaluating flux '
                    'ranges.')

            if hasattr(self.fva, 'columns'):
                fva_results = self.fva
            else:
                fva_results = flux_variability_analysis(
                    self.model,
                    reaction_list=boundary_rxns,
                    fraction_of_optimum=self.fva)

            # save old index
            old_index = flux_summary.index
            # generate new index for consistency with fva_results
            flux_summary['rxn_id'] = flux_summary.apply(
                lambda x: x.reaction.id, axis=1)
            # change to new index
            flux_summary.set_index(['rxn_id'], inplace=True)
            flux_summary = pd.concat([flux_summary, fva_results],
                                     axis=1,
                                     sort=False)
            flux_summary.rename(columns={
                'maximum': 'fmax',
                'minimum': 'fmin'
            },
                                inplace=True)
            # revert to old index
            flux_summary.set_index(old_index)

            def set_min_and_max(row):
                """Scale and set proper min and max values for flux."""
                fmax = row.reaction.metabolites[row.metabolite] * row.fmax
                fmin = row.reaction.metabolites[row.metabolite] * row.fmin

                if abs(fmin) <= abs(fmax):
                    row.fmax = fmax
                    row.fmin = fmin
                else:
                    # Reverse fluxes
                    row.fmax = fmin
                    row.fmin = fmax

                return row

            flux_summary = flux_summary.apply(set_min_and_max, axis=1)

        flux_summary = self._process_flux_dataframe(flux_summary)

        flux_summary = pd.concat([flux_summary, obj_df], sort=False)

        return flux_summary