def find_blocked_reactions(model,
                           reaction_list=None,
                           zero_cutoff=None,
                           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
        (default model.tolerance).
    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.

    """
    zero_cutoff = normalize_cutoff(model, zero_cutoff)

    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()

Find reactions that cannot carry any flux .


def find_essential_genes(model, threshold=None, processes=None):
    """
    Return a set of essential genes.

    A gene is considered essential if restricting the flux of all reactions
    that depend on it to zero causes the objective, e.g., the growth rate,
    to also be zero, below the threshold, or infeasible.

    Parameters
    ----------
    model : cobra.Model
        The model to find the essential genes for.
    threshold : float, optional
        Minimal objective flux to be considered viable. By default this is
        1% of the maximal objective.
    processes : int, optional
        The number of parallel processes to run. If not passed,
        will be set to the number of CPUs found.
    processes : int, optional
        The number of parallel processes to run. Can speed up the computations
        if the number of knockouts to perform is large. If not explicitly
        passed, it will be set from the global configuration singleton.

    Returns
    -------
    set
        Set of essential genes
    """
    if threshold is None:
        threshold = model.slim_optimize(error_value=None) * 1E-02
    deletions = single_gene_deletion(model, method='fba', processes=processes)
    essential = deletions.loc[deletions['growth'].isna() |
                              (deletions['growth'] < threshold), :].index
    return {model.genes.get_by_id(g) for ids in essential for g in ids}

def find_essential_genes ( model , threshold = None , processes = None ) : if threshold is None : threshold = model . slim_optimize ( error_value = None ) * 1E-02 deletions = single_gene_deletion ( model , method = 'fba' , processes = processes ) essential = deletions . loc [ deletions [ 'growth' ] . isna ( ) | ( deletions [ 'growth' ] < threshold ) , : ] . index return { model . genes . get_by_id ( g ) for ids in essential for g in ids }


def find_essential_reactions(model, threshold=None, processes=None):
    """Return a set of essential reactions.

    A reaction is considered essential if restricting its flux to zero
    causes the objective, e.g., the growth rate, to also be zero, below the
    threshold, or infeasible.

    Parameters
    ----------
    model : cobra.Model
        The model to find the essential reactions for.
    threshold : float, optional
        Minimal objective flux to be considered viable. By default this is
        1% of the maximal objective.
    processes : int, optional
        The number of parallel processes to run. Can speed up the computations
        if the number of knockouts to perform is large. If not explicitly
        passed, it will be set from the global configuration singleton.

    Returns
    -------
    set
        Set of essential reactions
    """
    if threshold is None:
        threshold = model.slim_optimize(error_value=None) * 1E-02
    deletions = single_reaction_deletion(
        model, method='fba', processes=processes)
    essential = deletions.loc[deletions['growth'].isna() |
                              (deletions['growth'] < threshold), :].index
    return {model.reactions.get_by_id(r) for ids in essential for r in ids}

def find_essential_reactions ( model , threshold = None , processes = None ) : if threshold is None : threshold = model . slim_optimize ( error_value = None ) * 1E-02 deletions = single_reaction_deletion ( model , method = 'fba' , processes = processes ) essential = deletions . loc [ deletions [ 'growth' ] . isna ( ) | ( deletions [ 'growth' ] < threshold ) , : ] . index return { model . reactions . get_by_id ( r ) for ids in essential for r in ids }


def add_SBO(model):
    """adds SBO terms for demands and exchanges

    This works for models which follow the standard convention for
    constructing and naming these reactions.

    The reaction should only contain the single metabolite being exchanged,
    and the id should be EX_metid or DM_metid
    """
    for r in model.reactions:
        # don't annotate already annotated reactions
        if r.annotation.get("sbo"):
            continue
        # only doing exchanges
        if len(r.metabolites) != 1:
            continue
        met_id = list(r._metabolites)[0].id
        if r.id.startswith("EX_") and r.id == "EX_" + met_id:
            r.annotation["sbo"] = "SBO:0000627"
        elif r.id.startswith("DM_") and r.id == "DM_" + met_id:
            r.annotation["sbo"] = "SBO:0000628"


def weight(self):
        """Calculate the mol mass of the compound

        Returns
        -------
        float
            the mol mass
        """
        try:
            return sum([count * elements_and_molecular_weights[element]
                        for element, count in self.elements.items()])
        except KeyError as e:
            warn("The element %s does not appear in the periodic table" % e)


def build_hugo_md(filename, tag, bump):
    """
    Build the markdown release notes for Hugo.

    Inserts the required TOML header with specific values and adds a break
    for long release notes.

    Parameters
    ----------
    filename : str, path
        The release notes file.
    tag : str
        The tag, following semantic versioning, of the current release.
    bump : {"major", "minor", "patch", "alpha", "beta"}
        The type of release.

    """
    header = [
        '+++\n',
        'date = "{}"\n'.format(date.today().isoformat()),
        'title = "{}"\n'.format(tag),
        'author = "The COBRApy Team"\n',
        'release = "{}"\n'.format(bump),
        '+++\n',
        '\n'
    ]
    with open(filename, "r") as file_h:
        content = insert_break(file_h.readlines())
    header.extend(content)
    with open(filename, "w") as file_h:
        file_h.writelines(header)

def
build_hugo_md
(
filename
,
tag
,
bump
)
:
header
=
[
'+++\n'
,
'date = "{}"\n'
.
format
(
date
.
today
(
)
.
isoformat
(
)
)
,
'title = "{}"\n'
.
format
(
tag
)
,
'author = "The COBRApy Team"\n'
,
'release = "{}"\n'
.
format
(
bump
)
,
'+++\n'
,
'\n'
]
with
open
(
filename
,
"r"
)
as
file_h
:
content
=
insert_break
(
file_h
.
readlines
(
)
)
header
.
extend
(
content
)
with
open
(
filename
,
"w"
)
as
file_h
:
file_h
.
writelines
(
header
)

Build the markdown release notes for Hugo .


def find_bump(target, tag):
    """Identify the kind of release by comparing to existing ones."""
    tmp = tag.split(".")
    existing = [intify(basename(f)) for f in glob(join(target, "[0-9]*.md"))]
    latest = max(existing)
    if int(tmp[0]) > latest[0]:
        return "major"
    elif int(tmp[1]) > latest[1]:
        return "minor"
    else:
        return "patch"

def find_bump ( target , tag ) : tmp = tag . split ( "." ) existing = [ intify ( basename ( f ) ) for f in glob ( join ( target , "[0-9]*.md" ) ) ] latest = max ( existing ) if int ( tmp [ 0 ] ) > latest [ 0 ] : return "major" elif int ( tmp [ 1 ] ) > latest [ 1 ] : return "minor" else : return "patch"

Identify the kind of release by comparing to existing ones .


def main(argv):
    """
    Identify the release type and create a new target file with TOML header.

    Requires three arguments.

    """
    source, target, tag = argv
    if "a" in tag:
        bump = "alpha"
    if "b" in tag:
        bump = "beta"
    else:
        bump = find_bump(target, tag)
    filename = "{}.md".format(tag)
    destination = copy(join(source, filename), target)
    build_hugo_md(destination, tag, bump)

def main ( argv ) : source , target , tag = argv if "a" in tag : bump = "alpha" if "b" in tag : bump = "beta" else : bump = find_bump ( target , tag ) filename = "{}.md" . format ( tag ) destination = copy ( join ( source , filename ) , target ) build_hugo_md ( destination , tag , bump )

Identify the release type and create a new target file with TOML header .


def _multi_deletion(model, entity, element_lists, method="fba",
                    solution=None, processes=None, **kwargs):
    """
    Provide a common interface for single or multiple knockouts.

    Parameters
    ----------
    model : cobra.Model
        The metabolic model to perform deletions in.
    entity : 'gene' or 'reaction'
        The entity to knockout (``cobra.Gene`` or ``cobra.Reaction``).
    element_lists : list
        List of iterables ``cobra.Reaction``s or ``cobra.Gene``s (or their IDs)
        to be deleted.
    method: {"fba", "moma", "linear moma", "room", "linear room"}, optional
        Method used to predict the growth rate.
    solution : cobra.Solution, optional
        A previous solution to use as a reference for (linear) MOMA or ROOM.
    processes : int, optional
        The number of parallel processes to run. Can speed up the computations
        if the number of knockouts to perform is large. If not passed,
        will be set to the number of CPUs found.
    kwargs :
        Passed on to underlying simulation functions.

    Returns
    -------
    pandas.DataFrame
        A representation of all combinations of entity deletions. The
        columns are 'growth' and 'status', where

        index : frozenset([str])
            The gene or reaction identifiers that were knocked out.
        growth : float
            The growth rate of the adjusted model.
        status : str
            The solution's status.
    """
    solver = sutil.interface_to_str(model.problem.__name__)
    if method == "moma" and solver not in sutil.qp_solvers:
        raise RuntimeError(
            "Cannot use MOMA since '{}' is not QP-capable."
            "Please choose a different solver or use FBA only.".format(solver))

    if processes is None:
        processes = CONFIGURATION.processes

    with model:
        if "moma" in method:
            add_moma(model, solution=solution, linear="linear" in method)
        elif "room" in method:
            add_room(model, solution=solution, linear="linear" in method,
                     **kwargs)

        args = set([frozenset(comb) for comb in product(*element_lists)])
        processes = min(processes, len(args))

        def extract_knockout_results(result_iter):
            result = pd.DataFrame([
                (frozenset(ids), growth, status)
                for (ids, growth, status) in result_iter
            ], columns=['ids', 'growth', 'status'])
            result.set_index('ids', inplace=True)
            return result

        if processes > 1:
            worker = dict(gene=_gene_deletion_worker,
                          reaction=_reaction_deletion_worker)[entity]
            chunk_size = len(args) // processes
            pool = multiprocessing.Pool(
                processes, initializer=_init_worker, initargs=(model,)
            )
            results = extract_knockout_results(pool.imap_unordered(
                worker,
                args,
                chunksize=chunk_size
            ))
            pool.close()
            pool.join()
        else:
            worker = dict(gene=_gene_deletion,
                          reaction=_reaction_deletion)[entity]
            results = extract_knockout_results(map(
                partial(worker, model), args))
        return results

Provide a common interface for single or multiple knockouts .


def single_reaction_deletion(model, reaction_list=None, method="fba",
                             solution=None, processes=None, **kwargs):
    """
    Knock out each reaction from a given list.

    Parameters
    ----------
    model : cobra.Model
        The metabolic model to perform deletions in.
    reaction_list : iterable, optional
        ``cobra.Reaction``s to be deleted. If not passed,
        all the reactions from the model are used.
    method: {"fba", "moma", "linear moma", "room", "linear room"}, optional
        Method used to predict the growth rate.
    solution : cobra.Solution, optional
        A previous solution to use as a reference for (linear) MOMA or ROOM.
    processes : int, optional
        The number of parallel processes to run. Can speed up the computations
        if the number of knockouts to perform is large. If not passed,
        will be set to the number of CPUs found.
    kwargs :
        Keyword arguments are passed on to underlying simulation functions
        such as ``add_room``.

    Returns
    -------
    pandas.DataFrame
        A representation of all single reaction deletions. The columns are
        'growth' and 'status', where

        index : frozenset([str])
            The reaction identifier that was knocked out.
        growth : float
            The growth rate of the adjusted model.
        status : str
            The solution's status.

    """
    return _multi_deletion(
        model, 'reaction',
        element_lists=_element_lists(model.reactions, reaction_list),
        method=method, solution=solution, processes=processes, **kwargs)

def single_reaction_deletion ( model , reaction_list = None , method = "fba" , solution = None , processes = None , * * kwargs ) : return _multi_deletion ( model , 'reaction' , element_lists = _element_lists ( model . reactions , reaction_list ) , method = method , solution = solution , processes = processes , * * kwargs )

Knock out each reaction from a given list .


def single_gene_deletion(model, gene_list=None, method="fba", solution=None,
                         processes=None, **kwargs):
    """
    Knock out each gene from a given list.

    Parameters
    ----------
    model : cobra.Model
        The metabolic model to perform deletions in.
    gene_list : iterable
        ``cobra.Gene``s to be deleted. If not passed,
        all the genes from the model are used.
    method: {"fba", "moma", "linear moma", "room", "linear room"}, optional
        Method used to predict the growth rate.
    solution : cobra.Solution, optional
        A previous solution to use as a reference for (linear) MOMA or ROOM.
    processes : int, optional
        The number of parallel processes to run. Can speed up the computations
        if the number of knockouts to perform is large. If not passed,
        will be set to the number of CPUs found.
    kwargs :
        Keyword arguments are passed on to underlying simulation functions
        such as ``add_room``.

    Returns
    -------
    pandas.DataFrame
        A representation of all single gene deletions. The columns are
        'growth' and 'status', where

        index : frozenset([str])
            The gene identifier that was knocked out.
        growth : float
            The growth rate of the adjusted model.
        status : str
            The solution's status.

    """
    return _multi_deletion(
        model, 'gene', element_lists=_element_lists(model.genes, gene_list),
        method=method, solution=solution, processes=processes, **kwargs)

def single_gene_deletion ( model , gene_list = None , method = "fba" , solution = None , processes = None , * * kwargs ) : return _multi_deletion ( model , 'gene' , element_lists = _element_lists ( model . genes , gene_list ) , method = method , solution = solution , processes = processes , * * kwargs )


def double_reaction_deletion(model, reaction_list1=None, reaction_list2=None,
                             method="fba", solution=None, processes=None,
                             **kwargs):
    """
    Knock out each reaction pair from the combinations of two given lists.

    We say 'pair' here but the order order does not matter.

    Parameters
    ----------
    model : cobra.Model
        The metabolic model to perform deletions in.
    reaction_list1 : iterable, optional
        First iterable of ``cobra.Reaction``s to be deleted. If not passed,
        all the reactions from the model are used.
    reaction_list2 : iterable, optional
        Second iterable of ``cobra.Reaction``s to be deleted. If not passed,
        all the reactions from the model are used.
    method: {"fba", "moma", "linear moma", "room", "linear room"}, optional
        Method used to predict the growth rate.
    solution : cobra.Solution, optional
        A previous solution to use as a reference for (linear) MOMA or ROOM.
    processes : int, optional
        The number of parallel processes to run. Can speed up the computations
        if the number of knockouts to perform is large. If not passed,
        will be set to the number of CPUs found.
    kwargs :
        Keyword arguments are passed on to underlying simulation functions
        such as ``add_room``.

    Returns
    -------
    pandas.DataFrame
        A representation of all combinations of reaction deletions. The
        columns are 'growth' and 'status', where

        index : frozenset([str])
            The reaction identifiers that were knocked out.
        growth : float
            The growth rate of the adjusted model.
        status : str
            The solution's status.

    """

    reaction_list1, reaction_list2 = _element_lists(model.reactions,
                                                    reaction_list1,
                                                    reaction_list2)
    return _multi_deletion(
        model, 'reaction', element_lists=[reaction_list1, reaction_list2],
        method=method, solution=solution, processes=processes, **kwargs)

def double_reaction_deletion ( model , reaction_list1 = None , reaction_list2 = None , method = "fba" , solution = None , processes = None , * * kwargs ) : reaction_list1 , reaction_list2 = _element_lists ( model . reactions , reaction_list1 , reaction_list2 ) return _multi_deletion ( model , 'reaction' , element_lists = [ reaction_list1 , reaction_list2 ] , method = method , solution = solution , processes = processes , * * kwargs )

Knock out each reaction pair from the combinations of two given lists .


def double_gene_deletion(model, gene_list1=None, gene_list2=None, method="fba",
                         solution=None, processes=None, **kwargs):
    """
    Knock out each gene pair from the combination of two given lists.

    We say 'pair' here but the order order does not matter.

    Parameters
    ----------
    model : cobra.Model
        The metabolic model to perform deletions in.
    gene_list1 : iterable, optional
        First iterable of ``cobra.Gene``s to be deleted. If not passed,
        all the genes from the model are used.
    gene_list2 : iterable, optional
        Second iterable of ``cobra.Gene``s to be deleted. If not passed,
        all the genes from the model are used.
    method: {"fba", "moma", "linear moma", "room", "linear room"}, optional
        Method used to predict the growth rate.
    solution : cobra.Solution, optional
        A previous solution to use as a reference for (linear) MOMA or ROOM.
    processes : int, optional
        The number of parallel processes to run. Can speed up the computations
        if the number of knockouts to perform is large. If not passed,
        will be set to the number of CPUs found.
    kwargs :
        Keyword arguments are passed on to underlying simulation functions
        such as ``add_room``.

    Returns
    -------
    pandas.DataFrame
        A representation of all combinations of gene deletions. The
        columns are 'growth' and 'status', where

        index : frozenset([str])
            The gene identifiers that were knocked out.
        growth : float
            The growth rate of the adjusted model.
        status : str
            The solution's status.

    """

    gene_list1, gene_list2 = _element_lists(model.genes, gene_list1,
                                            gene_list2)
    return _multi_deletion(
        model, 'gene', element_lists=[gene_list1, gene_list2],
        method=method, solution=solution, processes=processes, **kwargs)

def double_gene_deletion ( model , gene_list1 = None , gene_list2 = None , method = "fba" , solution = None , processes = None , * * kwargs ) : gene_list1 , gene_list2 = _element_lists ( model . genes , gene_list1 , gene_list2 ) return _multi_deletion ( model , 'gene' , element_lists = [ gene_list1 , gene_list2 ] , method = method , solution = solution , processes = processes , * * kwargs )

Knock out each gene pair from the combination of two given lists .


def reverse_id(self):
        """Generate the id of reverse_variable from the reaction's id."""
        return '_'.join((self.id, 'reverse',
                         hashlib.md5(
                             self.id.encode('utf-8')).hexdigest()[0:5]))

def reverse_id ( self ) : return '_' . join ( ( self . id , 'reverse' , hashlib . md5 ( self . id . encode ( 'utf-8' ) ) . hexdigest ( ) [ 0 : 5 ] ) )

Generate the id of reverse_variable from the reaction s id .


def flux(self):
        """
        The flux value in the most recent solution.

        Flux is the primal value of the corresponding variable in the model.

        Warnings
        --------
        * Accessing reaction fluxes through a `Solution` object is the safer,
          preferred, and only guaranteed to be correct way. You can see how to
          do so easily in the examples.
        * Reaction flux is retrieved from the currently defined
          `self._model.solver`. The solver status is checked but there are no
          guarantees that the current solver state is the one you are looking
          for.
        * If you modify the underlying model after an optimization, you will
          retrieve the old optimization values.

        Raises
        ------
        RuntimeError
            If the underlying model was never optimized beforehand or the
            reaction is not part of a model.
        OptimizationError
            If the solver status is anything other than 'optimal'.
        AssertionError
            If the flux value is not within the bounds.

        Examples
        --------
        >>> import cobra.test
        >>> model = cobra.test.create_test_model("textbook")
        >>> solution = model.optimize()
        >>> model.reactions.PFK.flux
        7.477381962160283
        >>> solution.fluxes.PFK
        7.4773819621602833
        """
        try:
            check_solver_status(self._model.solver.status)
            return self.forward_variable.primal - self.reverse_variable.primal
        except AttributeError:
            raise RuntimeError(
                "reaction '{}' is not part of a model".format(self.id))
        # Due to below all-catch, which sucks, need to reraise these.
        except (RuntimeError, OptimizationError) as err:
            raise_with_traceback(err)
        # Would love to catch CplexSolverError and GurobiError here.
        except Exception as err:
            raise_from(OptimizationError(
                "Likely no solution exists. Original solver message: {}."
                "".format(str(err))), err)

The flux value in the most recent solution .


def gene_name_reaction_rule(self):
        """Display gene_reaction_rule with names intead.

        Do NOT use this string for computation. It is intended to give a
        representation of the rule using more familiar gene names instead of
        the often cryptic ids.

        """
        names = {i.id: i.name for i in self._genes}
        ast = parse_gpr(self._gene_reaction_rule)[0]
        return ast2str(ast, names=names)

def gene_name_reaction_rule ( self ) : names = { i . id : i . name for i in self . _genes } ast = parse_gpr ( self . _gene_reaction_rule ) [ 0 ] return ast2str ( ast , names = names )

Display gene_reaction_rule with names intead .


def functional(self):
        """All required enzymes for reaction are functional.

        Returns
        -------
        bool
            True if the gene-protein-reaction (GPR) rule is fulfilled for
            this reaction, or if reaction is not associated to a model,
            otherwise False.
        """
        if self._model:
            tree, _ = parse_gpr(self.gene_reaction_rule)
            return eval_gpr(tree, {gene.id for gene in self.genes if
                                   not gene.functional})
        return True

def functional ( self ) : if self . _model : tree , _ = parse_gpr ( self . gene_reaction_rule ) return eval_gpr ( tree , { gene . id for gene in self . genes if not gene . functional } ) return True

All required enzymes for reaction are functional .


def _update_awareness(self):
        """Make sure all metabolites and genes that are associated with
        this reaction are aware of it.

        """
        for x in self._metabolites:
            x._reaction.add(self)
        for x in self._genes:
            x._reaction.add(self)

def _update_awareness ( self ) : for x in self . _metabolites : x . _reaction . add ( self ) for x in self . _genes : x . _reaction . add ( self )

Make sure all metabolites and genes that are associated with this reaction are aware of it .


def copy(self):
        """Copy a reaction

        The referenced metabolites and genes are also copied.

        """
        # no references to model when copying
        model = self._model
        self._model = None
        for i in self._metabolites:
            i._model = None
        for i in self._genes:
            i._model = None
        # now we can copy
        new_reaction = deepcopy(self)
        # restore the references
        self._model = model
        for i in self._metabolites:
            i._model = model
        for i in self._genes:
            i._model = model
        return new_reaction

def
copy
(
self
)
:
# no references to model when copying
model
=
self
.
_model
self
.
_model
=
for
i
in
self
.
_metabolites
:
i
.
_model
=
for
i
in
self
.
_genes
:
i
.
_model
=
# now we can copy
new_reaction
=
deepcopy
(
self
)
# restore the references
self
.
_model
=
model
for
i
in
self
.
_metabolites
:
i
.
_model
=
model
for
i
in
self
.
_genes
:
i
.
_model
=
model
return
new_reaction


def get_coefficient(self, metabolite_id):
        """
        Return the stoichiometric coefficient of a metabolite.

        Parameters
        ----------
        metabolite_id : str or cobra.Metabolite

        """
        if isinstance(metabolite_id, Metabolite):
            return self._metabolites[metabolite_id]

        _id_to_metabolites = {m.id: m for m in self._metabolites}
        return self._metabolites[_id_to_metabolites[metabolite_id]]

def get_coefficient ( self , metabolite_id ) : if isinstance ( metabolite_id , Metabolite ) : return self . _metabolites [ metabolite_id ] _id_to_metabolites = { m . id : m for m in self . _metabolites } return self . _metabolites [ _id_to_metabolites [ metabolite_id ] ]

Return the stoichiometric coefficient of a metabolite .


def add_metabolites(self, metabolites_to_add, combine=True,
                        reversibly=True):
        """Add metabolites and stoichiometric coefficients to the reaction.
        If the final coefficient for a metabolite is 0 then it is removed
        from the reaction.

        The change is reverted upon exit when using the model as a context.

        Parameters
        ----------
        metabolites_to_add : dict
            Dictionary with metabolite objects or metabolite identifiers as
            keys and coefficients as values. If keys are strings (name of a
            metabolite) the reaction must already be part of a model and a
            metabolite with the given name must exist in the model.

        combine : bool
            Describes behavior a metabolite already exists in the reaction.
            True causes the coefficients to be added.
            False causes the coefficient to be replaced.

        reversibly : bool
            Whether to add the change to the context to make the change
            reversibly or not (primarily intended for internal use).

        """
        old_coefficients = self.metabolites
        new_metabolites = []
        _id_to_metabolites = dict([(x.id, x) for x in self._metabolites])

        for metabolite, coefficient in iteritems(metabolites_to_add):

            # Make sure metabolites being added belong to the same model, or
            # else copy them.
            if isinstance(metabolite, Metabolite):
                if ((metabolite.model is not None) and
                        (metabolite.model is not self._model)):
                    metabolite = metabolite.copy()

            met_id = str(metabolite)
            # If a metabolite already exists in the reaction then
            # just add them.
            if met_id in _id_to_metabolites:
                reaction_metabolite = _id_to_metabolites[met_id]
                if combine:
                    self._metabolites[reaction_metabolite] += coefficient
                else:
                    self._metabolites[reaction_metabolite] = coefficient
            else:
                # If the reaction is in a model, ensure we aren't using
                # a duplicate metabolite.
                if self._model:
                    try:
                        metabolite = \
                            self._model.metabolites.get_by_id(met_id)
                    except KeyError as e:
                        if isinstance(metabolite, Metabolite):
                            new_metabolites.append(metabolite)
                        else:
                            # do we want to handle creation here?
                            raise e
                elif isinstance(metabolite, string_types):
                    # if we want to handle creation, this should be changed
                    raise ValueError("Reaction '%s' does not belong to a "
                                     "model. Either add the reaction to a "
                                     "model or use Metabolite objects instead "
                                     "of strings as keys."
                                     % self.id)
                self._metabolites[metabolite] = coefficient
                # make the metabolite aware that it is involved in this
                # reaction
                metabolite._reaction.add(self)

        # from cameo ...
        model = self.model
        if model is not None:
            model.add_metabolites(new_metabolites)

            for metabolite, coefficient in self._metabolites.items():
                model.constraints[
                    metabolite.id].set_linear_coefficients(
                    {self.forward_variable: coefficient,
                     self.reverse_variable: -coefficient
                     })

        for metabolite, the_coefficient in list(self._metabolites.items()):
            if the_coefficient == 0:
                # make the metabolite aware that it no longer participates
                # in this reaction
                metabolite._reaction.remove(self)
                self._metabolites.pop(metabolite)

        context = get_context(self)
        if context and reversibly:
            if combine:
                # Just subtract the metabolites that were added
                context(partial(
                    self.subtract_metabolites, metabolites_to_add,
                    combine=True, reversibly=False))
            else:
                # Reset them with add_metabolites
                mets_to_reset = {
                    key: old_coefficients[model.metabolites.get_by_any(key)[0]]
                    for key in iterkeys(metabolites_to_add)}

                context(partial(
                    self.add_metabolites, mets_to_reset,
                    combine=False, reversibly=False))

Add metabolites and stoichiometric coefficients to the reaction . If the final coefficient for a metabolite is 0 then it is removed from the reaction .


def subtract_metabolites(self, metabolites, combine=True, reversibly=True):
        """Subtract metabolites from a reaction.

        That means add the metabolites with -1*coefficient. If the final
        coefficient for a metabolite is 0 then the metabolite is removed from
        the reaction.

        Notes
        -----
        * A final coefficient < 0 implies a reactant.
        * The change is reverted upon exit when using the model as a context.

        Parameters
        ----------
        metabolites : dict
            Dictionary where the keys are of class Metabolite and the values
            are the coefficients. These metabolites will be added to the
            reaction.

        combine : bool
            Describes behavior a metabolite already exists in the reaction.
            True causes the coefficients to be added.
            False causes the coefficient to be replaced.

        reversibly : bool
            Whether to add the change to the context to make the change
            reversibly or not (primarily intended for internal use).

        """
        self.add_metabolites({
            k: -v for k, v in iteritems(metabolites)},
            combine=combine, reversibly=reversibly)

def subtract_metabolites ( self , metabolites , combine = True , reversibly = True ) : self . add_metabolites ( { k : - v for k , v in iteritems ( metabolites ) } , combine = combine , reversibly = reversibly )


def build_reaction_string(self, use_metabolite_names=False):
        """Generate a human readable reaction string"""

        def format(number):
            return "" if number == 1 else str(number).rstrip(".") + " "

        id_type = 'id'
        if use_metabolite_names:
            id_type = 'name'
        reactant_bits = []
        product_bits = []
        for met in sorted(self._metabolites, key=attrgetter("id")):
            coefficient = self._metabolites[met]
            name = str(getattr(met, id_type))
            if coefficient >= 0:
                product_bits.append(format(coefficient) + name)
            else:
                reactant_bits.append(format(abs(coefficient)) + name)

        reaction_string = ' + '.join(reactant_bits)
        if not self.reversibility:
            if self.lower_bound < 0 and self.upper_bound <= 0:
                reaction_string += ' <-- '
            else:
                reaction_string += ' --> '
        else:
            reaction_string += ' <=> '
        reaction_string += ' + '.join(product_bits)
        return reaction_string

def
build_reaction_string
(
self
,
use_metabolite_names
=
False
)
:
def
format
(
number
)
:
return
""
if
number
==
1
else
str
(
number
)
.
rstrip
(
"."
)
+
" "
id_type
=
'id'
if
use_metabolite_names
:
id_type
=
'name'
reactant_bits
=
[
]
product_bits
=
[
]
for
met
in
sorted
(
self
.
_metabolites
,
key
=
attrgetter
(
"id"
)
)
:
coefficient
=
self
.
_metabolites
[
met
]
name
=
str
(
getattr
(
met
,
id_type
)
)
if
coefficient
>=
0
:
product_bits
.
append
(
format
(
coefficient
)
+
name
)
else
:
reactant_bits
.
append
(
format
(
abs
(
coefficient
)
)
+
name
)
reaction_string
=
' + '
.
join
(
reactant_bits
)
if
not
self
.
reversibility
:
if
self
.
lower_bound
<
0
and
self
.
upper_bound
<=
0
:
reaction_string
+=
' <-- '
else
:
reaction_string
+=
' --> '
else
:
reaction_string
+=
' <=> '
reaction_string
+=
' + '
.
join
(
product_bits
)
return
reaction_string


def check_mass_balance(self):
        """Compute mass and charge balance for the reaction

        returns a dict of {element: amount} for unbalanced elements.
        "charge" is treated as an element in this dict
        This should be empty for balanced reactions.
        """
        reaction_element_dict = defaultdict(int)
        for metabolite, coefficient in iteritems(self._metabolites):
            if metabolite.charge is not None:
                reaction_element_dict["charge"] += \
                    coefficient * metabolite.charge
            if metabolite.elements is None:
                raise ValueError("No elements found in metabolite %s"
                                 % metabolite.id)
            for element, amount in iteritems(metabolite.elements):
                reaction_element_dict[element] += coefficient * amount
        # filter out 0 values
        return {k: v for k, v in iteritems(reaction_element_dict) if v != 0}

def
check_mass_balance
(
self
)
:
reaction_element_dict
=
defaultdict
(
int
)
for
metabolite
,
coefficient
in
iteritems
(
self
.
_metabolites
)
:
if
metabolite
.
charge
is
not
:
reaction_element_dict
[
"charge"
]
+=
coefficient
*
metabolite
.
charge
if
metabolite
.
elements
is
:
raise
ValueError
(
"No elements found in metabolite %s"
%
metabolite
.
id
)
for
element
,
amount
in
iteritems
(
metabolite
.
elements
)
:
reaction_element_dict
[
element
]
+=
coefficient
*
amount
# filter out 0 values
return
{
k
:
v
for
k
,
v
in
iteritems
(
reaction_element_dict
)
if
v
!=
0
}


def compartments(self):
        """lists compartments the metabolites are in"""
        if self._compartments is None:
            self._compartments = {met.compartment for met in self._metabolites
                                  if met.compartment is not None}
        return self._compartments

def compartments ( self ) : if self . _compartments is None : self . _compartments = { met . compartment for met in self . _metabolites if met . compartment is not None } return self . _compartments


def _associate_gene(self, cobra_gene):
        """Associates a cobra.Gene object with a cobra.Reaction.

        Parameters
        ----------
        cobra_gene : cobra.core.Gene.Gene

        """
        self._genes.add(cobra_gene)
        cobra_gene._reaction.add(self)
        cobra_gene._model = self._model

def _associate_gene ( self , cobra_gene ) : self . _genes . add ( cobra_gene ) cobra_gene . _reaction . add ( self ) cobra_gene . _model = self . _model

Associates a cobra . Gene object with a cobra . Reaction .


def _dissociate_gene(self, cobra_gene):
        """Dissociates a cobra.Gene object with a cobra.Reaction.

        Parameters
        ----------
        cobra_gene : cobra.core.Gene.Gene

        """
        self._genes.discard(cobra_gene)
        cobra_gene._reaction.discard(self)

def _dissociate_gene ( self , cobra_gene ) : self . _genes . discard ( cobra_gene ) cobra_gene . _reaction . discard ( self )

Dissociates a cobra . Gene object with a cobra . Reaction .


def build_reaction_from_string(self, reaction_str, verbose=True,
                                   fwd_arrow=None, rev_arrow=None,
                                   reversible_arrow=None, term_split="+"):
        """Builds reaction from reaction equation reaction_str using parser

        Takes a string and using the specifications supplied in the optional
        arguments infers a set of metabolites, metabolite compartments and
        stoichiometries for the reaction.  It also infers the reversibility
        of the reaction from the reaction arrow.

        Changes to the associated model are reverted upon exit when using
        the model as a context.

        Parameters
        ----------
        reaction_str : string
            a string containing a reaction formula (equation)
        verbose: bool
            setting verbosity of function
        fwd_arrow : re.compile
            for forward irreversible reaction arrows
        rev_arrow : re.compile
            for backward irreversible reaction arrows
        reversible_arrow : re.compile
            for reversible reaction arrows
        term_split : string
            dividing individual metabolite entries

        """
        # set the arrows
        forward_arrow_finder = _forward_arrow_finder if fwd_arrow is None \
            else re.compile(re.escape(fwd_arrow))
        reverse_arrow_finder = _reverse_arrow_finder if rev_arrow is None \
            else re.compile(re.escape(rev_arrow))
        reversible_arrow_finder = _reversible_arrow_finder \
            if reversible_arrow is None \
            else re.compile(re.escape(reversible_arrow))
        if self._model is None:
            warn("no model found")
            model = None
        else:
            model = self._model
        found_compartments = compartment_finder.findall(reaction_str)
        if len(found_compartments) == 1:
            compartment = found_compartments[0]
            reaction_str = compartment_finder.sub("", reaction_str)
        else:
            compartment = ""

        # reversible case
        arrow_match = reversible_arrow_finder.search(reaction_str)
        if arrow_match is not None:
            self.lower_bound = -1000
            self.upper_bound = 1000
        else:  # irreversible
            # try forward
            arrow_match = forward_arrow_finder.search(reaction_str)
            if arrow_match is not None:
                self.upper_bound = 1000
                self.lower_bound = 0
            else:
                # must be reverse
                arrow_match = reverse_arrow_finder.search(reaction_str)
                if arrow_match is None:
                    raise ValueError("no suitable arrow found in '%s'" %
                                     reaction_str)
                else:
                    self.upper_bound = 0
                    self.lower_bound = -1000
        reactant_str = reaction_str[:arrow_match.start()].strip()
        product_str = reaction_str[arrow_match.end():].strip()

        self.subtract_metabolites(self.metabolites, combine=True)

        for substr, factor in ((reactant_str, -1), (product_str, 1)):
            if len(substr) == 0:
                continue
            for term in substr.split(term_split):
                term = term.strip()
                if term.lower() == "nothing":
                    continue
                if " " in term:
                    num_str, met_id = term.split()
                    num = float(num_str.lstrip("(").rstrip(")")) * factor
                else:
                    met_id = term
                    num = factor
                met_id += compartment
                try:
                    met = model.metabolites.get_by_id(met_id)
                except KeyError:
                    if verbose:
                        print("unknown metabolite '%s' created" % met_id)
                    met = Metabolite(met_id)
                self.add_metabolites({met: num})

def
build_reaction_from_string
(
self
,
reaction_str
,
verbose
=
True
,
fwd_arrow
=
,
rev_arrow
=
,
reversible_arrow
=
,
term_split
=
"+"
)
:
# set the arrows
forward_arrow_finder
=
_forward_arrow_finder
if
fwd_arrow
is
else
re
.
compile
(
re
.
escape
(
fwd_arrow
)
)
reverse_arrow_finder
=
_reverse_arrow_finder
if
rev_arrow
is
else
re
.
compile
(
re
.
escape
(
rev_arrow
)
)
reversible_arrow_finder
=
_reversible_arrow_finder
if
reversible_arrow
is
else
re
.
compile
(
re
.
escape
(
reversible_arrow
)
)
if
self
.
_model
is
:
warn
(
"no model found"
)
model
=
else
:
model
=
self
.
_model
found_compartments
=
compartment_finder
.
findall
(
reaction_str
)
if
len
(
found_compartments
)
==
1
:
compartment
=
found_compartments
[
0
]
reaction_str
=
compartment_finder
.
sub
(
""
,
reaction_str
)
else
:
compartment
=
""
# reversible case
arrow_match
=
reversible_arrow_finder
.
search
(
reaction_str
)
if
arrow_match
is
not
:
self
.
lower_bound
=
1000
self
.
upper_bound
=
1000
else
:
# irreversible
# try forward
arrow_match
=
forward_arrow_finder
.
search
(
reaction_str
)
if
arrow_match
is
not
:
self
.
upper_bound
=
1000
self
.
lower_bound
=
0
else
:
# must be reverse
arrow_match
=
reverse_arrow_finder
.
search
(
reaction_str
)
if
arrow_match
is
:
raise
ValueError
(
"no suitable arrow found in '%s'"
%
reaction_str
)
else
:
self
.
upper_bound
=
0
self
.
lower_bound
=
1000
reactant_str
=
reaction_str
[
:
arrow_match
.
start
(
)
]
.
strip
(
)
product_str
=
reaction_str
[
arrow_match
.
end
(
)
:
]
.
strip
(
)
self
.
subtract_metabolites
(
self
.
metabolites
,
combine
=
True
)
for
substr
,
factor
in
(
(
reactant_str
,
1
)
,
(
product_str
,
1
)
)
:
if
len
(
substr
)
==
0
:
continue
for
term
in
substr
.
split
(
term_split
)
:
term
=
term
.
strip
(
)
if
term
.
lower
(
)
==
"nothing"
:
continue
if
" "
in
term
:
num_str
,
met_id
=
term
.
split
(
)
num
=
float
(
num_str
.
lstrip
(
"("
)
.
rstrip
(
")"
)
)
*
factor
else
:
met_id
=
term
num
=
factor
met_id
+=
compartment
try
:
met
=
model
.
metabolites
.
get_by_id
(
met_id
)
except
KeyError
:
if
verbose
:
print
(
"unknown metabolite '%s' created"
%
met_id
)
met
=
Metabolite
(
met_id
)
self
.
add_metabolites
(
{
met
:
num
}
)


def _clip(sid, prefix):
    """Clips a prefix from the beginning of a string if it exists."""
    return sid[len(prefix):] if sid.startswith(prefix) else sid

def _clip ( sid , prefix ) : return sid [ len ( prefix ) : ] if sid . startswith ( prefix ) else sid

Clips a prefix from the beginning of a string if it exists .


def _f_gene(sid, prefix="G_"):
    """Clips gene prefix from id."""
    sid = sid.replace(SBML_DOT, ".")
    return _clip(sid, prefix)

def _f_gene ( sid , prefix = "G_" ) : sid = sid . replace ( SBML_DOT , "." ) return _clip ( sid , prefix )


def read_sbml_model(filename, number=float, f_replace=F_REPLACE,
                    set_missing_bounds=False, **kwargs):
    """Reads SBML model from given filename.

    If the given filename ends with the suffix ''.gz'' (for example,
    ''myfile.xml.gz'),' the file is assumed to be compressed in gzip
    format and will be automatically decompressed upon reading. Similarly,
    if the given filename ends with ''.zip'' or ''.bz2',' the file is
    assumed to be compressed in zip or bzip2 format (respectively).  Files
    whose names lack these suffixes will be read uncompressed.  Note that
    if the file is in zip format but the archive contains more than one
    file, only the first file in the archive will be read and the rest
    ignored.

    To read a gzip/zip file, libSBML needs to be configured and linked
    with the zlib library at compile time.  It also needs to be linked
    with the bzip2 library to read files in bzip2 format.  (Both of these
    are the default configurations for libSBML.)

    This function supports SBML with FBC-v1 and FBC-v2. FBC-v1 models
    are converted to FBC-v2 models before reading.

    The parser tries to fall back to information in notes dictionaries
    if information is not available in the FBC packages, e.g.,
    CHARGE, FORMULA on species, or GENE_ASSOCIATION, SUBSYSTEM on reactions.

    Parameters
    ----------
    filename : path to SBML file, or SBML string, or SBML file handle
        SBML which is read into cobra model
    number: data type of stoichiometry: {float, int}
        In which data type should the stoichiometry be parsed.
    f_replace : dict of replacement functions for id replacement
        Dictionary of replacement functions for gene, specie, and reaction.
        By default the following id changes are performed on import:
        clip G_ from genes, clip M_ from species, clip R_ from reactions
        If no replacements should be performed, set f_replace={}, None
    set_missing_bounds : boolean flag to set missing bounds
        Missing bounds are set to default bounds in configuration.

    Returns
    -------
    cobra.core.Model

    Notes
    -----
    Provided file handles cannot be opened in binary mode, i.e., use
        with open(path, "r" as f):
            read_sbml_model(f)
    File handles to compressed files are not supported yet.
    """
    try:
        doc = _get_doc_from_filename(filename)
        return _sbml_to_model(doc,
                              number=number,
                              f_replace=f_replace,
                              set_missing_bounds=set_missing_bounds,
                              **kwargs)
    except IOError as e:
        raise e

    except Exception:
        LOGGER.error(traceback.print_exc())
        raise CobraSBMLError(
            "Something went wrong reading the SBML model. Most likely the SBML"
            " model is not valid. Please check that your model is valid using "
            "the `cobra.io.sbml.validate_sbml_model` function or via the "
            "online validator at http://sbml.org/validator .\n"
            "\t`(model, errors) = validate_sbml_model(filename)`"
            "\nIf the model is valid and cannot be read please open an issue "
            "at https://github.com/opencobra/cobrapy/issues .")


def _get_doc_from_filename(filename):
    """Get SBMLDocument from given filename.

    Parameters
    ----------
    filename : path to SBML, or SBML string, or filehandle

    Returns
    -------
    libsbml.SBMLDocument
    """
    if isinstance(filename, string_types):
        if ("win" in platform) and (len(filename) < 260) \
                and os.path.exists(filename):
            # path (win)
            doc = libsbml.readSBMLFromFile(filename)  # noqa: E501 type: libsbml.SBMLDocument
        elif ("win" not in platform) and os.path.exists(filename):
            # path other
            doc = libsbml.readSBMLFromFile(filename)  # noqa: E501 type: libsbml.SBMLDocument
        else:
            # string representation
            if "<sbml" not in filename:
                raise IOError("The file with 'filename' does not exist, "
                              "or is not an SBML string. Provide the path to "
                              "an existing SBML file or a valid SBML string "
                              "representation: \n%s", filename)

            doc = libsbml.readSBMLFromString(filename)  # noqa: E501 type: libsbml.SBMLDocument

    elif hasattr(filename, "read"):
        # file handle
        doc = libsbml.readSBMLFromString(filename.read())  # noqa: E501 type: libsbml.SBMLDocument
    else:
        raise CobraSBMLError("Input type '%s' for 'filename' is not supported."
                             " Provide a path, SBML str, "
                             "or file handle.", type(filename))

    return doc


def write_sbml_model(cobra_model, filename, f_replace=F_REPLACE, **kwargs):
    """Writes cobra model to filename.

    The created model is SBML level 3 version 1 (L1V3) with
    fbc package v2 (fbc-v2).

    If the given filename ends with the suffix ".gz" (for example,
    "myfile.xml.gz"), libSBML assumes the caller wants the file to be
    written compressed in gzip format. Similarly, if the given filename
    ends with ".zip" or ".bz2", libSBML assumes the caller wants the
    file to be compressed in zip or bzip2 format (respectively). Files
    whose names lack these suffixes will be written uncompressed. Special
    considerations for the zip format: If the given filename ends with
    ".zip", the file placed in the zip archive will have the suffix
    ".xml" or ".sbml".  For example, the file in the zip archive will
    be named "test.xml" if the given filename is "test.xml.zip" or
    "test.zip". Similarly, the filename in the archive will be
    "test.sbml" if the given filename is "test.sbml.zip".

    Parameters
    ----------
    cobra_model : cobra.core.Model
        Model instance which is written to SBML
    filename : string
        path to which the model is written
    use_fbc_package : boolean {True, False}
        should the fbc package be used
    f_replace: dict of replacement functions for id replacement
    """
    doc = _model_to_sbml(cobra_model, f_replace=f_replace, **kwargs)

    if isinstance(filename, string_types):
        # write to path
        libsbml.writeSBMLToFile(doc, filename)

    elif hasattr(filename, "write"):
        # write to file handle
        sbml_str = libsbml.writeSBMLToString(doc)
        filename.write(sbml_str)

def
write_sbml_model
(
cobra_model
,
filename
,
f_replace
=
F_REPLACE
,
*
*
kwargs
)
:
doc
=
_model_to_sbml
(
cobra_model
,
f_replace
=
f_replace
,
*
*
kwargs
)
if
isinstance
(
filename
,
string_types
)
:
# write to path
libsbml
.
writeSBMLToFile
(
doc
,
filename
)
elif
hasattr
(
filename
,
"write"
)
:
# write to file handle
sbml_str
=
libsbml
.
writeSBMLToString
(
doc
)
filename
.
write
(
sbml_str
)


def _create_bound(model, reaction, bound_type, f_replace, units=None,
                  flux_udef=None):
    """Creates bound in model for given reaction.

    Adds the parameters for the bounds to the SBML model.

    Parameters
    ----------
    model : libsbml.Model
        SBML model instance
    reaction : cobra.core.Reaction
        Cobra reaction instance from which the bounds are read.
    bound_type : {LOWER_BOUND, UPPER_BOUND}
        Type of bound
    f_replace : dict of id replacement functions
    units : flux units

    Returns
    -------
    Id of bound parameter.
    """
    value = getattr(reaction, bound_type)
    if value == config.lower_bound:
        return LOWER_BOUND_ID
    elif value == 0:
        return ZERO_BOUND_ID
    elif value == config.upper_bound:
        return UPPER_BOUND_ID
    elif value == -float("Inf"):
        return BOUND_MINUS_INF
    elif value == float("Inf"):
        return BOUND_PLUS_INF
    else:
        # new parameter
        rid = reaction.id
        if f_replace and F_REACTION_REV in f_replace:
            rid = f_replace[F_REACTION_REV](rid)
        pid = rid + "_" + bound_type
        _create_parameter(model, pid=pid, value=value, sbo=SBO_FLUX_BOUND,
                          units=units, flux_udef=flux_udef)
        return pid

def
_create_bound
(
model
,
reaction
,
bound_type
,
f_replace
,
units
=
,
flux_udef
=
)
:
value
=
getattr
(
reaction
,
bound_type
)
if
value
==
config
.
lower_bound
:
return
LOWER_BOUND_ID
elif
value
==
0
:
return
ZERO_BOUND_ID
elif
value
==
config
.
upper_bound
:
return
UPPER_BOUND_ID
elif
value
==
float
(
"Inf"
)
:
return
BOUND_MINUS_INF
elif
value
==
float
(
"Inf"
)
:
return
BOUND_PLUS_INF
else
:
# new parameter
rid
=
reaction
.
id
if
f_replace
and
F_REACTION_REV
in
f_replace
:
rid
=
f_replace
[
F_REACTION_REV
]
(
rid
)
pid
=
rid
+
"_"
+
bound_type
_create_parameter
(
model
,
pid
=
pid
,
value
=
value
,
sbo
=
SBO_FLUX_BOUND
,
units
=
units
,
flux_udef
=
flux_udef
)
return
pid

Creates bound in model for given reaction .


def _create_parameter(model, pid, value, sbo=None, constant=True, units=None,
                      flux_udef=None):
    """Create parameter in SBML model."""
    parameter = model.createParameter()  # type: libsbml.Parameter
    parameter.setId(pid)
    parameter.setValue(value)
    parameter.setConstant(constant)
    if sbo:
        parameter.setSBOTerm(sbo)
    if units:
        parameter.setUnits(flux_udef.getId())

def
_create_parameter
(
model
,
pid
,
value
,
sbo
=
,
constant
=
True
,
units
=
,
flux_udef
=
)
:
parameter
=
model
.
createParameter
(
)
# type: libsbml.Parameter
parameter
.
setId
(
pid
)
parameter
.
setValue
(
value
)
parameter
.
setConstant
(
constant
)
if
sbo
:
parameter
.
setSBOTerm
(
sbo
)
if
units
:
parameter
.
setUnits
(
flux_udef
.
getId
(
)
)


def _check_required(sbase, value, attribute):
    """Get required attribute from SBase.

    Parameters
    ----------
    sbase : libsbml.SBase
    value : existing value
    attribute: name of attribute

    Returns
    -------
    attribute value (or value if already set)
    """

    if (value is None) or (value == ""):
        msg = "Required attribute '%s' cannot be found or parsed in '%s'" % \
              (attribute, sbase)
        if hasattr(sbase, "getId") and sbase.getId():
            msg += " with id '%s'" % sbase.getId()
        elif hasattr(sbase, "getName") and sbase.getName():
            msg += " with name '%s'" % sbase.getName()
        elif hasattr(sbase, "getMetaId") and sbase.getMetaId():
            msg += " with metaId '%s'" % sbase.getName()
        raise CobraSBMLError(msg)
    return value


def _check(value, message):
    """
    Checks the libsbml return value and logs error messages.

    If 'value' is None, logs an error message constructed using
      'message' and then exits with status code 1. If 'value' is an integer,
      it assumes it is a libSBML return status code. If the code value is
      LIBSBML_OPERATION_SUCCESS, returns without further action; if it is not,
      prints an error message constructed using 'message' along with text from
      libSBML explaining the meaning of the code, and exits with status code 1.

    """
    if value is None:
        LOGGER.error('Error: LibSBML returned a null value trying '
                     'to <' + message + '>.')
    elif type(value) is int:
        if value == libsbml.LIBSBML_OPERATION_SUCCESS:
            return
        else:
            LOGGER.error('Error encountered trying to <' + message + '>.')
            LOGGER.error('LibSBML error code {}: {}'.format(str(value),
                         libsbml.OperationReturnValue_toString(value).strip()))
    else:
        return

Checks the libsbml return value and logs error messages .


def _parse_notes_dict(sbase):
    """ Creates dictionary of COBRA notes.

    Parameters
    ----------
    sbase : libsbml.SBase

    Returns
    -------
    dict of notes
    """
    notes = sbase.getNotesString()
    if notes and len(notes) > 0:
        pattern = r"<p>\s*(\w+\s*\w*)\s*:\s*([\w|\s]+)<"
        matches = re.findall(pattern, notes)
        d = {k.strip(): v.strip() for (k, v) in matches}
        return {k: v for k, v in d.items() if len(v) > 0}
    else:
        return {}

def _parse_notes_dict ( sbase ) : notes = sbase . getNotesString ( ) if notes and len ( notes ) > 0 : pattern = r"<p>\s*(\w+\s*\w*)\s*:\s*([\w|\s]+)<" matches = re . findall ( pattern , notes ) d = { k . strip ( ) : v . strip ( ) for ( k , v ) in matches } return { k : v for k , v in d . items ( ) if len ( v ) > 0 } else : return { }


def _sbase_notes_dict(sbase, notes):
    """Set SBase notes based on dictionary.

    Parameters
    ----------
    sbase : libsbml.SBase
        SBML object to set notes on
    notes : notes object
        notes information from cobra object
    """
    if notes and len(notes) > 0:
        tokens = [''] + \
            ["
{}: {}
".format(k, v) for (k, v) in notes.items()] + \
            [""]
        _check(
            sbase.setNotes("\n".join(tokens)),
            "Setting notes on sbase: {}".format(sbase)
        )

'<html xmlns = "http://www.w3.org/1999/xhtml" >'


def _parse_annotations(sbase):
    """Parses cobra annotations from a given SBase object.

    Annotations are dictionaries with the providers as keys.

    Parameters
    ----------
    sbase : libsbml.SBase
        SBase from which the SBML annotations are read

    Returns
    -------
    dict (annotation dictionary)

    FIXME: annotation format must be updated (this is a big collection of
          fixes) - see: https://github.com/opencobra/cobrapy/issues/684)
    """
    annotation = {}

    # SBO term
    if sbase.isSetSBOTerm():
        # FIXME: correct handling of annotations
        annotation["sbo"] = sbase.getSBOTermID()

    # RDF annotation
    cvterms = sbase.getCVTerms()
    if cvterms is None:
        return annotation

    for cvterm in cvterms:  # type: libsbml.CVTerm
        for k in range(cvterm.getNumResources()):
            # FIXME: read and store the qualifier

            uri = cvterm.getResourceURI(k)
            match = URL_IDENTIFIERS_PATTERN.match(uri)
            if not match:
                LOGGER.warning("%s does not conform to "
                               "http(s)://identifiers.org/collection/id", uri)
                continue

            provider, identifier = match.group(1), match.group(2)
            if provider in annotation:
                if isinstance(annotation[provider], string_types):
                    annotation[provider] = [annotation[provider]]
                # FIXME: use a list
                if identifier not in annotation[provider]:
                    annotation[provider].append(identifier)
            else:
                # FIXME: always in list
                annotation[provider] = identifier

    return annotation

Parses cobra annotations from a given SBase object .


def _sbase_annotations(sbase, annotation):
    """Set SBase annotations based on cobra annotations.

    Parameters
    ----------
    sbase : libsbml.SBase
        SBML object to annotate
    annotation : cobra annotation structure
        cobra object with annotation information

    FIXME: annotation format must be updated
        (https://github.com/opencobra/cobrapy/issues/684)
    """

    if not annotation or len(annotation) == 0:
        return

    # standardize annotations
    annotation_data = deepcopy(annotation)

    for key, value in annotation_data.items():
        # handling of non-string annotations (e.g. integers)
        if isinstance(value, (float, int)):
            value = str(value)
        if isinstance(value, string_types):
            annotation_data[key] = [("is", value)]

    for key, value in annotation_data.items():
        for idx, item in enumerate(value):
            if isinstance(item, string_types):
                value[idx] = ("is", item)

    # set metaId
    meta_id = "meta_{}".format(sbase.getId())
    sbase.setMetaId(meta_id)

    # rdf_items = []
    for provider, data in iteritems(annotation_data):

        # set SBOTerm
        if provider in ["SBO", "sbo"]:
            if provider == "SBO":
                LOGGER.warning("'SBO' provider is deprecated, "
                               "use 'sbo' provider instead")
            sbo_term = data[0][1]
            _check(sbase.setSBOTerm(sbo_term),
                   "Setting SBOTerm: {}".format(sbo_term))

            # FIXME: sbo should also be written as CVTerm
            continue

        for item in data:
            qualifier_str, entity = item[0], item[1]
            qualifier = QUALIFIER_TYPES.get(qualifier_str, None)
            if qualifier is None:
                qualifier = libsbml.BQB_IS
                LOGGER.error("Qualifier type is not supported on "
                             "annotation: '{}'".format(qualifier_str))

            qualifier_type = libsbml.BIOLOGICAL_QUALIFIER
            if qualifier_str.startswith("bqm_"):
                qualifier_type = libsbml.MODEL_QUALIFIER

            cv = libsbml.CVTerm()  # type: libsbml.CVTerm
            cv.setQualifierType(qualifier_type)
            if qualifier_type == libsbml.BIOLOGICAL_QUALIFIER:
                cv.setBiologicalQualifierType(qualifier)
            elif qualifier_type == libsbml.MODEL_QUALIFIER:
                cv.setModelQualifierType(qualifier)
            else:
                raise CobraSBMLError('Unsupported qualifier: '
                                     '%s' % qualifier)
            resource = "%s/%s/%s" % (URL_IDENTIFIERS_PREFIX, provider, entity)
            cv.addResource(resource)
            _check(sbase.addCVTerm(cv),
                   "Setting cvterm: {}, resource: {}".format(cv, resource))

Set SBase annotations based on cobra annotations .


def _error_string(error, k=None):
    """String representation of SBMLError.

    Parameters
    ----------
    error : libsbml.SBMLError
    k : index of error

    Returns
    -------
    string representation of error
    """
    package = error.getPackage()
    if package == '':
        package = 'core'

    template = 'E{} ({}): {} ({}, L{}); {}; {}'
    error_str = template.format(k, error.getSeverityAsString(),
                                error.getCategoryAsString(), package,
                                error.getLine(), error.getShortMessage(),
                                error.getMessage())
    return error_str

def
_error_string
(
error
,
k
=
)
:
package
=
error
.
getPackage
(
)
if
package
==
''
:
package
=
'core'
template
=
'E{} ({}): {} ({}, L{}); {}; {}'
error_str
=
template
.
format
(
k
,
error
.
getSeverityAsString
(
)
,
error
.
getCategoryAsString
(
)
,
package
,
error
.
getLine
(
)
,
error
.
getShortMessage
(
)
,
error
.
getMessage
(
)
)
return
error_str


def production_envelope(model, reactions, objective=None, carbon_sources=None,
                        points=20, threshold=None):
    """Calculate the objective value conditioned on all combinations of
    fluxes for a set of chosen reactions

    The production envelope can be used to analyze a model's ability to
    produce a given compound conditional on the fluxes for another set of
    reactions, such as the uptake rates. The model is alternately optimized
    with respect to minimizing and maximizing the objective and the
    obtained fluxes are recorded. Ranges to compute production is set to the
    effective
    bounds, i.e., the minimum / maximum fluxes that can be obtained given
    current reaction bounds.

    Parameters
    ----------
    model : cobra.Model
        The model to compute the production envelope for.
    reactions : list or string
        A list of reactions, reaction identifiers or a single reaction.
    objective : string, dict, model.solver.interface.Objective, optional
        The objective (reaction) to use for the production envelope. Use the
        model's current objective if left missing.
    carbon_sources : list or string, optional
       One or more reactions or reaction identifiers that are the source of
       carbon for computing carbon (mol carbon in output over mol carbon in
       input) and mass yield (gram product over gram output). Only objectives
       with a carbon containing input and output metabolite is supported.
       Will identify active carbon sources in the medium if none are specified.
    points : int, optional
       The number of points to calculate production for.
    threshold : float, optional
        A cut-off under which flux values will be considered to be zero
        (default model.tolerance).

    Returns
    -------
    pandas.DataFrame
        A data frame with one row per evaluated point and

        - reaction id : one column per input reaction indicating the flux at
          each given point,
        - carbon_source: identifiers of carbon exchange reactions

        A column for the maximum and minimum each for the following types:

        - flux: the objective flux
        - carbon_yield: if carbon source is defined and the product is a
          single metabolite (mol carbon product per mol carbon feeding source)
        - mass_yield: if carbon source is defined and the product is a
          single metabolite (gram product per 1 g of feeding source)

    Examples
    --------
    >>> import cobra.test
    >>> from cobra.flux_analysis import production_envelope
    >>> model = cobra.test.create_test_model("textbook")
    >>> production_envelope(model, ["EX_glc__D_e", "EX_o2_e"])

    """

    reactions = model.reactions.get_by_any(reactions)
    objective = model.solver.objective if objective is None else objective
    data = dict()

    if carbon_sources is None:
        c_input = find_carbon_sources(model)
    else:
        c_input = model.reactions.get_by_any(carbon_sources)

    if c_input is None:
        data['carbon_source'] = None
    elif hasattr(c_input, 'id'):
        data['carbon_source'] = c_input.id
    else:
        data['carbon_source'] = ', '.join(rxn.id for rxn in c_input)

    threshold = normalize_cutoff(model, threshold)

    size = points ** len(reactions)

    for direction in ('minimum', 'maximum'):
        data['flux_{}'.format(direction)] = full(size, nan, dtype=float)
        data['carbon_yield_{}'.format(direction)] = full(
            size, nan, dtype=float)
        data['mass_yield_{}'.format(direction)] = full(
            size, nan, dtype=float)

    grid = pd.DataFrame(data)

    with model:
        model.objective = objective
        objective_reactions = list(sutil.linear_reaction_coefficients(model))

        if len(objective_reactions) != 1:
            raise ValueError('cannot calculate yields for objectives with '
                             'multiple reactions')
        c_output = objective_reactions[0]
        min_max = fva(model, reactions, fraction_of_optimum=0)
        min_max[min_max.abs() < threshold] = 0.0
        points = list(product(*[
            linspace(min_max.at[rxn.id, "minimum"],
                     min_max.at[rxn.id, "maximum"],
                     points, endpoint=True) for rxn in reactions]))
        tmp = pd.DataFrame(points, columns=[rxn.id for rxn in reactions])
        grid = pd.concat([grid, tmp], axis=1, copy=False)
        add_envelope(model, reactions, grid, c_input, c_output, threshold)

    return grid


def total_yield(input_fluxes, input_elements, output_flux, output_elements):
    """
    Compute total output per input unit.

    Units are typically mol carbon atoms or gram of source and product.

    Parameters
    ----------
    input_fluxes : list
        A list of input reaction fluxes in the same order as the
        ``input_components``.
    input_elements : list
        A list of reaction components which are in turn list of numbers.
    output_flux : float
        The output flux value.
    output_elements : list
        A list of stoichiometrically weighted output reaction components.

    Returns
    -------
    float
        The ratio between output (mol carbon atoms or grams of product) and
        input (mol carbon atoms or grams of source compounds).
    """

    carbon_input_flux = sum(
        total_components_flux(flux, components, consumption=True)
        for flux, components in zip(input_fluxes, input_elements))
    carbon_output_flux = total_components_flux(
        output_flux, output_elements, consumption=False)
    try:
        return carbon_output_flux / carbon_input_flux
    except ZeroDivisionError:
        return nan

def total_yield ( input_fluxes , input_elements , output_flux , output_elements ) : carbon_input_flux = sum ( total_components_flux ( flux , components , consumption = True ) for flux , components in zip ( input_fluxes , input_elements ) ) carbon_output_flux = total_components_flux ( output_flux , output_elements , consumption = False ) try : return carbon_output_flux / carbon_input_flux except ZeroDivisionError : return nan


def reaction_elements(reaction):
    """
    Split metabolites into the atoms times their stoichiometric coefficients.

    Parameters
    ----------
    reaction : Reaction
        The metabolic reaction whose components are desired.

    Returns
    -------
    list
        Each of the reaction's metabolites' desired carbon elements (if any)
        times that metabolite's stoichiometric coefficient.
    """
    c_elements = [coeff * met.elements.get('C', 0)
                  for met, coeff in iteritems(reaction.metabolites)]
    return [elem for elem in c_elements if elem != 0]

def reaction_elements ( reaction ) : c_elements = [ coeff * met . elements . get ( 'C' , 0 ) for met , coeff in iteritems ( reaction . metabolites ) ] return [ elem for elem in c_elements if elem != 0 ]

Split metabolites into the atoms times their stoichiometric coefficients .


def reaction_weight(reaction):
    """Return the metabolite weight times its stoichiometric coefficient."""

    if len(reaction.metabolites) != 1:
        raise ValueError('Reaction weight is only defined for single '
                         'metabolite products or educts.')

    met, coeff = next(iteritems(reaction.metabolites))

    return [coeff * met.formula_weight]

Return the metabolite weight times its stoichiometric coefficient .


def total_components_flux(flux, components, consumption=True):
    """
    Compute the total components consumption or production flux.

    Parameters
    ----------
    flux : float
        The reaction flux for the components.
    components : list
        List of stoichiometrically weighted components.
    consumption : bool, optional
        Whether to sum up consumption or production fluxes.

    """

    direction = 1 if consumption else -1
    c_flux = [elem * flux * direction for elem in components]

    return sum([flux for flux in c_flux if flux > 0])

def total_components_flux ( flux , components , consumption = True ) : direction = 1 if consumption else - 1 c_flux = [ elem * flux * direction for elem in components ] return sum ( [ flux for flux in c_flux if flux > 0 ] )

Compute the total components consumption or production flux .


def find_carbon_sources(model):
    """
    Find all active carbon source reactions.

    Parameters
    ----------
    model : Model
        A genome-scale metabolic model.

    Returns
    -------
    list
       The medium reactions with carbon input flux.

    """

    try:
        model.slim_optimize(error_value=None)
    except OptimizationError:
        return []

    reactions = model.reactions.get_by_any(list(model.medium))
    reactions_fluxes = [
        (rxn, total_components_flux(rxn.flux, reaction_elements(rxn),
                                    consumption=True)) for rxn in reactions]
    return [rxn for rxn, c_flux in reactions_fluxes if c_flux > 0]

def find_carbon_sources ( model ) : try : model . slim_optimize ( error_value = None ) except OptimizationError : return [ ] reactions = model . reactions . get_by_any ( list ( model . medium ) ) reactions_fluxes = [ ( rxn , total_components_flux ( rxn . flux , reaction_elements ( rxn ) , consumption = True ) ) for rxn in reactions ] return [ rxn for rxn , c_flux in reactions_fluxes if c_flux > 0 ]

Find all active carbon source reactions .


def assess(model, reaction, flux_coefficient_cutoff=0.001, solver=None):
    """Assesses production capacity.

    Assesses the capacity of the model to produce the precursors for the
    reaction and absorb the production of the reaction while the reaction is
    operating at, or above, the specified cutoff.

    Parameters
    ----------
    model : cobra.Model
        The cobra model to assess production capacity for

    reaction : reaction identifier or cobra.Reaction
        The reaction to assess

    flux_coefficient_cutoff :  float
        The minimum flux that reaction must carry to be considered active.

    solver : basestring
        Solver name. If None, the default solver will be used.

    Returns
    -------
    bool or dict
        True if the model can produce the precursors and absorb the products
        for the reaction operating at, or above, flux_coefficient_cutoff.
        Otherwise, a dictionary of {'precursor': Status, 'product': Status}.
        Where Status is the results from assess_precursors and
        assess_products, respectively.

    """
    reaction = model.reactions.get_by_any(reaction)[0]
    with model as m:
        m.objective = reaction
        if _optimize_or_value(m, solver=solver) >= flux_coefficient_cutoff:
            return True
        else:
            results = dict()
            results['precursors'] = assess_component(
                model, reaction, 'reactants', flux_coefficient_cutoff)
            results['products'] = assess_component(
                model, reaction, 'products', flux_coefficient_cutoff)
            return results

def assess ( model , reaction , flux_coefficient_cutoff = 0.001 , solver = None ) : reaction = model . reactions . get_by_any ( reaction ) [ 0 ] with model as m : m . objective = reaction if _optimize_or_value ( m , solver = solver ) >= flux_coefficient_cutoff : return True else : results = dict ( ) results [ 'precursors' ] = assess_component ( model , reaction , 'reactants' , flux_coefficient_cutoff ) results [ 'products' ] = assess_component ( model , reaction , 'products' , flux_coefficient_cutoff ) return results


def assess_component(model, reaction, side, flux_coefficient_cutoff=0.001,
                     solver=None):
    """Assesses the ability of the model to provide sufficient precursors,
    or absorb products, for a reaction operating at, or beyond,
    the specified cutoff.

    Parameters
    ----------
    model : cobra.Model
        The cobra model to assess production capacity for

    reaction : reaction identifier or cobra.Reaction
        The reaction to assess

    side : basestring
        Side of the reaction, 'products' or 'reactants'

    flux_coefficient_cutoff :  float
        The minimum flux that reaction must carry to be considered active.

    solver : basestring
        Solver name. If None, the default solver will be used.

    Returns
    -------
    bool or dict
        True if the precursors can be simultaneously produced at the
        specified cutoff. False, if the model has the capacity to produce
        each individual precursor at the specified threshold  but not all
        precursors at the required level simultaneously. Otherwise a
        dictionary of the required and the produced fluxes for each reactant
        that is not produced in sufficient quantities.

    """
    reaction = model.reactions.get_by_any(reaction)[0]
    result_key = dict(reactants='produced', products='capacity')[side]
    get_components = attrgetter(side)
    with model as m:
        m.objective = reaction
        if _optimize_or_value(m, solver=solver) >= flux_coefficient_cutoff:
            return True
        simulation_results = {}
        # build the demand reactions and add all at once
        demand_reactions = {}
        for component in get_components(reaction):
            coeff = reaction.metabolites[component]
            demand = m.add_boundary(component, type='demand')
            demand.metabolites[component] = coeff
            demand_reactions[demand] = (component, coeff)
        # First assess whether all precursors can be produced simultaneously
        joint_demand = Reaction("joint_demand")
        for demand_reaction in demand_reactions:
            joint_demand += demand_reaction
        m.add_reactions([joint_demand])
        m.objective = joint_demand
        if _optimize_or_value(m, solver=solver) >= flux_coefficient_cutoff:
            return True

        # Otherwise assess the ability of the model to produce each precursor
        # individually.  Now assess the ability of the model to produce each
        # reactant for a reaction
        for demand_reaction, (component, coeff) in iteritems(demand_reactions):
            # Calculate the maximum amount of the
            with m:
                m.objective = demand_reaction
                flux = _optimize_or_value(m, solver=solver)
            # metabolite that can be produced.
            if flux_coefficient_cutoff > flux:
                # Scale the results to a single unit
                simulation_results.update({
                    component: {
                        'required': flux_coefficient_cutoff / abs(coeff),
                        result_key: flux / abs(coeff)
                    }})
        if len(simulation_results) == 0:
            simulation_results = False
        return simulation_results

Assesses the ability of the model to provide sufficient precursors or absorb products for a reaction operating at or beyond the specified cutoff .


def assess_precursors(model, reaction, flux_coefficient_cutoff=0.001,
                      solver=None):
    """Assesses the ability of the model to provide sufficient precursors for
    a reaction operating at, or beyond, the specified cutoff.

    Deprecated: use assess_component instead

    Parameters
    ----------
    model : cobra.Model
        The cobra model to assess production capacity for

    reaction : reaction identifier or cobra.Reaction
        The reaction to assess

    flux_coefficient_cutoff :  float
        The minimum flux that reaction must carry to be considered active.

    solver : basestring
        Solver name. If None, the default solver will be used.

    Returns
    -------
    bool or dict
        True if the precursors can be simultaneously produced at the
        specified cutoff. False, if the model has the capacity to produce
        each individual precursor at the specified threshold  but not all
        precursors at the required level simultaneously. Otherwise a
        dictionary of the required and the produced fluxes for each reactant
        that is not produced in sufficient quantities.

    """
    warn('use assess_component instead', DeprecationWarning)
    return assess_component(model, reaction, 'reactants',
                            flux_coefficient_cutoff, solver)

def
assess_precursors
(
model
,
reaction
,
flux_coefficient_cutoff
=
0.001
,
solver
=
)
:
warn
(
'use assess_component instead'
,
DeprecationWarning
)
return
assess_component
(
model
,
reaction
,
'reactants'
,
flux_coefficient_cutoff
,
solver
)

Assesses the ability of the model to provide sufficient precursors for a reaction operating at or beyond the specified cutoff .


def assess_products(model, reaction, flux_coefficient_cutoff=0.001,
                    solver=None):
    """Assesses whether the model has the capacity to absorb the products of
    a reaction at a given flux rate.

    Useful for identifying which components might be blocking a reaction
    from achieving a specific flux rate.

    Deprecated: use assess_component instead

    Parameters
    ----------
    model : cobra.Model
        The cobra model to assess production capacity for

    reaction : reaction identifier or cobra.Reaction
        The reaction to assess

    flux_coefficient_cutoff :  float
        The minimum flux that reaction must carry to be considered active.

    solver : basestring
        Solver name. If None, the default solver will be used.

    Returns
    -------
    bool or dict
        True if the model has the capacity to absorb all the reaction
        products being simultaneously given the specified cutoff.   False,
        if the model has the capacity to absorb each individual product but
        not all products at the required level simultaneously.   Otherwise a
        dictionary of the required and the capacity fluxes for each product
        that is not absorbed in sufficient quantities.

    """
    warn('use assess_component instead', DeprecationWarning)
    return assess_component(model, reaction, 'products',
                            flux_coefficient_cutoff, solver)

def
assess_products
(
model
,
reaction
,
flux_coefficient_cutoff
=
0.001
,
solver
=
)
:
warn
(
'use assess_component instead'
,
DeprecationWarning
)
return
assess_component
(
model
,
reaction
,
'products'
,
flux_coefficient_cutoff
,
solver
)

Assesses whether the model has the capacity to absorb the products of a reaction at a given flux rate .


def add_loopless(model, zero_cutoff=None):
    """Modify a model so all feasible flux distributions are loopless.

    In most cases you probably want to use the much faster `loopless_solution`.
    May be used in cases where you want to add complex constraints and
    objecives (for instance quadratic objectives) to the model afterwards
    or use an approximation of Gibbs free energy directions in you model.
    Adds variables and constraints to a model which will disallow flux
    distributions with loops. The used formulation is described in [1]_.
    This function *will* modify your model.

    Parameters
    ----------
    model : cobra.Model
        The model to which to add the constraints.
    zero_cutoff : positive float, optional
        Cutoff used for null space. Coefficients with an absolute value smaller
        than `zero_cutoff` are considered to be zero (default model.tolerance).

    Returns
    -------
    Nothing

    References
    ----------
    .. [1] Elimination of thermodynamically infeasible loops in steady-state
       metabolic models. Schellenberger J, Lewis NE, Palsson BO. Biophys J.
       2011 Feb 2;100(3):544-53. doi: 10.1016/j.bpj.2010.12.3707. Erratum
       in: Biophys J. 2011 Mar 2;100(5):1381.
    """
    zero_cutoff = normalize_cutoff(model, zero_cutoff)

    internal = [i for i, r in enumerate(model.reactions) if not r.boundary]
    s_int = create_stoichiometric_matrix(model)[:, numpy.array(internal)]
    n_int = nullspace(s_int).T
    max_bound = max(max(abs(b) for b in r.bounds) for r in model.reactions)
    prob = model.problem

    # Add indicator variables and new constraints
    to_add = []
    for i in internal:
        rxn = model.reactions[i]
        # indicator variable a_i
        indicator = prob.Variable("indicator_" + rxn.id, type="binary")
        # -M*(1 - a_i) <= v_i <= M*a_i
        on_off_constraint = prob.Constraint(
            rxn.flux_expression - max_bound * indicator,
            lb=-max_bound, ub=0, name="on_off_" + rxn.id)
        # -(max_bound + 1) * a_i + 1 <= G_i <= -(max_bound + 1) * a_i + 1000
        delta_g = prob.Variable("delta_g_" + rxn.id)
        delta_g_range = prob.Constraint(
            delta_g + (max_bound + 1) * indicator,
            lb=1, ub=max_bound, name="delta_g_range_" + rxn.id)
        to_add.extend([indicator, on_off_constraint, delta_g, delta_g_range])

    model.add_cons_vars(to_add)

    # Add nullspace constraints for G_i
    for i, row in enumerate(n_int):
        name = "nullspace_constraint_" + str(i)
        nullspace_constraint = prob.Constraint(Zero, lb=0, ub=0, name=name)
        model.add_cons_vars([nullspace_constraint])
        coefs = {model.variables[
                 "delta_g_" + model.reactions[ridx].id]: row[i]
                 for i, ridx in enumerate(internal) if
                 abs(row[i]) > zero_cutoff}
        model.constraints[name].set_linear_coefficients(coefs)

Modify a model so all feasible flux distributions are loopless .


def _add_cycle_free(model, fluxes):
    """Add constraints for CycleFreeFlux."""
    model.objective = model.solver.interface.Objective(
        Zero, direction="min", sloppy=True)
    objective_vars = []
    for rxn in model.reactions:
        flux = fluxes[rxn.id]
        if rxn.boundary:
            rxn.bounds = (flux, flux)
            continue
        if flux >= 0:
            rxn.bounds = max(0, rxn.lower_bound), max(flux, rxn.upper_bound)
            objective_vars.append(rxn.forward_variable)
        else:
            rxn.bounds = min(flux, rxn.lower_bound), min(0, rxn.upper_bound)
            objective_vars.append(rxn.reverse_variable)

    model.objective.set_linear_coefficients({v: 1.0 for v in objective_vars})

def _add_cycle_free ( model , fluxes ) : model . objective = model . solver . interface . Objective ( Zero , direction = "min" , sloppy = True ) objective_vars = [ ] for rxn in model . reactions : flux = fluxes [ rxn . id ] if rxn . boundary : rxn . bounds = ( flux , flux ) continue if flux >= 0 : rxn . bounds = max ( 0 , rxn . lower_bound ) , max ( flux , rxn . upper_bound ) objective_vars . append ( rxn . forward_variable ) else : rxn . bounds = min ( flux , rxn . lower_bound ) , min ( 0 , rxn . upper_bound ) objective_vars . append ( rxn . reverse_variable ) model . objective . set_linear_coefficients ( { v : 1.0 for v in objective_vars } )


def loopless_solution(model, fluxes=None):
    """Convert an existing solution to a loopless one.

    Removes as many loops as possible (see Notes).
    Uses the method from CycleFreeFlux [1]_ and is much faster than
    `add_loopless` and should therefore be the preferred option to get loopless
    flux distributions.

    Parameters
    ----------
    model : cobra.Model
        The model to which to add the constraints.
    fluxes : dict
        A dictionary {rxn_id: flux} that assigns a flux to each reaction. If
        not None will use the provided flux values to obtain a close loopless
        solution.

    Returns
    -------
    cobra.Solution
        A solution object containing the fluxes with the least amount of
        loops possible or None if the optimization failed (usually happening
        if the flux distribution in `fluxes` is infeasible).

    Notes
    -----
    The returned flux solution has the following properties:

    - it contains the minimal number of loops possible and no loops at all if
      all flux bounds include zero
    - it has an objective value close to the original one and the same
      objective value id the objective expression can not form a cycle
      (which is usually true since it consumes metabolites)
    - it has the same exact exchange fluxes as the previous solution
    - all fluxes have the same sign (flow in the same direction) as the
      previous solution

    References
    ----------
    .. [1] 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.
    """
    # Need to reoptimize otherwise spurious solution artifacts can cause
    # all kinds of havoc
    # TODO: check solution status
    if fluxes is None:
        sol = model.optimize(objective_sense=None)
        fluxes = sol.fluxes

    with model:
        prob = model.problem
        # Needs one fixed bound for cplex...
        loopless_obj_constraint = prob.Constraint(
            model.objective.expression,
            lb=-1e32, name="loopless_obj_constraint")
        model.add_cons_vars([loopless_obj_constraint])
        _add_cycle_free(model, fluxes)
        solution = model.optimize(objective_sense=None)
        solution.objective_value = loopless_obj_constraint.primal

    return solution

Convert an existing solution to a loopless one .


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

Plugin to get a loopless FVA solution from single FVA iteration .


def create_stoichiometric_matrix(model, array_type='dense', dtype=None):
    """Return a stoichiometric array representation of the given model.

    The the columns represent the reactions and rows represent
    metabolites. S[i,j] therefore contains the quantity of metabolite `i`
    produced (negative for consumed) by reaction `j`.

    Parameters
    ----------
    model : cobra.Model
        The cobra model to construct the matrix for.
    array_type : string
        The type of array to construct. if 'dense', return a standard
        numpy.array, 'dok', or 'lil' will construct a sparse array using
        scipy of the corresponding type and 'DataFrame' will give a
        pandas `DataFrame` with metabolite indices and reaction columns
    dtype : data-type
        The desired data-type for the array. If not given, defaults to float.

    Returns
    -------
    matrix of class `dtype`
        The stoichiometric matrix for the given model.
    """
    if array_type not in ('DataFrame', 'dense') and not dok_matrix:
        raise ValueError('Sparse matrices require scipy')

    if dtype is None:
        dtype = np.float64

    array_constructor = {
        'dense': np.zeros, 'dok': dok_matrix, 'lil': lil_matrix,
        'DataFrame': np.zeros,
    }

    n_metabolites = len(model.metabolites)
    n_reactions = len(model.reactions)
    array = array_constructor[array_type]((n_metabolites, n_reactions),
                                          dtype=dtype)

    m_ind = model.metabolites.index
    r_ind = model.reactions.index

    for reaction in model.reactions:
        for metabolite, stoich in iteritems(reaction.metabolites):
            array[m_ind(metabolite), r_ind(reaction)] = stoich

    if array_type == 'DataFrame':
        metabolite_ids = [met.id for met in model.metabolites]
        reaction_ids = [rxn.id for rxn in model.reactions]
        return pd.DataFrame(array, index=metabolite_ids, columns=reaction_ids)

    else:
        return array

def
create_stoichiometric_matrix
(
model
,
array_type
=
'dense'
,
dtype
=
)
:
if
array_type
not
in
(
'DataFrame'
,
'dense'
)
and
not
dok_matrix
:
raise
ValueError
(
'Sparse matrices require scipy'
)
if
dtype
is
:
dtype
=
np
.
float64
array_constructor
=
{
'dense'
:
np
.
zeros
,
'dok'
:
dok_matrix
,
'lil'
:
lil_matrix
,
'DataFrame'
:
np
.
zeros
,
}
n_metabolites
=
len
(
model
.
metabolites
)
n_reactions
=
len
(
model
.
reactions
)
array
=
array_constructor
[
array_type
]
(
(
n_metabolites
,
n_reactions
)
,
dtype
=
dtype
)
m_ind
=
model
.
metabolites
.
index
r_ind
=
model
.
reactions
.
index
for
reaction
in
model
.
reactions
:
for
metabolite
,
stoich
in
iteritems
(
reaction
.
metabolites
)
:
array
[
m_ind
(
metabolite
)
,
r_ind
(
reaction
)
]
=
stoich
if
array_type
==
'DataFrame'
:
metabolite_ids
=
[
met
.
id
for
met
in
model
.
metabolites
]
reaction_ids
=
[
rxn
.
id
for
rxn
in
model
.
reactions
]
return
pd
.
DataFrame
(
array
,
index
=
metabolite_ids
,
columns
=
reaction_ids
)
else
:
return
array

Return a stoichiometric array representation of the given model .


def nullspace(A, atol=1e-13, rtol=0):
    """Compute an approximate basis for the nullspace of A.
    The algorithm used by this function is based on the singular value
    decomposition of `A`.

    Parameters
    ----------
    A : numpy.ndarray
        A should be at most 2-D.  A 1-D array with length k will be treated
        as a 2-D with shape (1, k)
    atol : float
        The absolute tolerance for a zero singular value.  Singular values
        smaller than `atol` are considered to be zero.
    rtol : float
        The relative tolerance.  Singular values less than rtol*smax are
        considered to be zero, where smax is the largest singular value.

    If both `atol` and `rtol` are positive, the combined tolerance is the
    maximum of the two; that is::
    tol = max(atol, rtol * smax)
    Singular values smaller than `tol` are considered to be zero.

    Returns
    -------
    numpy.ndarray
        If `A` is an array with shape (m, k), then `ns` will be an array
        with shape (k, n), where n is the estimated dimension of the
        nullspace of `A`.  The columns of `ns` are a basis for the
        nullspace; each element in numpy.dot(A, ns) will be approximately
        zero.

    Notes
    -----
    Taken from the numpy cookbook.
    """
    A = np.atleast_2d(A)
    u, s, vh = np.linalg.svd(A)
    tol = max(atol, rtol * s[0])
    nnz = (s >= tol).sum()
    ns = vh[nnz:].conj().T
    return ns

def nullspace ( A , atol = 1e-13 , rtol = 0 ) : A = np . atleast_2d ( A ) u , s , vh = np . linalg . svd ( A ) tol = max ( atol , rtol * s [ 0 ] ) nnz = ( s >= tol ) . sum ( ) ns = vh [ nnz : ] . conj ( ) . T return ns

Compute an approximate basis for the nullspace of A . The algorithm used by this function is based on the singular value decomposition of A .