Exemplo n.º 1
0
def _apply_min_growth(community, min_growth, atol=1e-6, rtol=1e-6):
    """Set minimum growth constraints on a model.

    Will integrate with the context.
    """
    context = get_context(community)

    def reset(taxon, lb):
        logger.info("resetting growth rate constraint for %s" % taxon)
        community.constraints["objective_" + taxon].ub = None
        community.constraints["objective_" + taxon].lb = lb

    for sp in community.taxa:
        logger.info("setting growth rate constraint for %s" % sp)
        obj = community.constraints["objective_" + sp]
        if context:
            context(partial(reset, sp, obj.lb))
        if min_growth[sp] > atol:
            obj.lb = (1.0 - rtol) * min_growth[sp] - atol
        else:
            logger.info(
                "minimal growth rate smaller than tolerance,"
                " setting to zero."
            )
            obj.lb = 0
Exemplo n.º 2
0
def add_cons_vars_to_problem(
    model: "Model",
    what: Union[List[CONS_VARS], Tuple[CONS_VARS], Components],
    **kwargs,
) -> None:
    """Add variables and constraints to a model's solver object.

    Useful for variables and constraints that can not be expressed with
    reactions and lower/upper bounds. It will integrate with the model's
    context manager in order to revert changes upon leaving the context.

    Parameters
    ----------
    model : cobra.Model
       The model to which to add the variables and constraints.
    what : list or tuple of optlang.interface.Variable or
           optlang.interface.Constraint
       The variables and constraints to add to the model.
    **kwargs : keyword arguments
       Keyword arguments passed to solver's add() method.

    """
    model.solver.add(what, **kwargs)

    context = get_context(model)
    if context:
        context(partial(model.solver.remove, what))
Exemplo n.º 3
0
    def add_proteins(self, protein_list: Iterator[Protein]):
        """Add proteins to the model, in the same fashion as `.add_metabollites`."""
        if not hasattr(protein_list, "__iter__"):
            protein_list = [protein_list]
        if len(protein_list) == 0:
            return None
        # Take left difference
        pruned = [x for x in protein_list if x.id not in self.proteins]
        for prot in pruned:
            prot._model = self

        to_add = []
        for prot in pruned:
            if prot.id not in self.constraints:
                constraint = self.problem.Constraint(Zero,
                                                     name=prot.id,
                                                     lb=0,
                                                     ub=0)
                to_add += [constraint]

        self.add_cons_vars(to_add)

        context = get_context(self)
        if context:
            context(partial(self.proteins.__isub__, pruned))
            for x in pruned:
                context(partial(setattr, x, "_model", None))

        self.proteins += pruned
        self._populate_solver([], None, pruned)
Exemplo n.º 4
0
    def __imul__(self, coefficient):
        """Scale coefficients in a reaction by a given value

        E.g. A -> B becomes 2A -> 2B.

        If coefficient is less than zero, the reaction is reversed and the
        bounds are swapped.
        """
        self._metabolites = {
            met: value * coefficient
            for met, value in self._metabolites.items()
        }

        if coefficient < 0:
            self.bounds = (-self.upper_bound, -self.lower_bound)

        if self._model:
            self._model._populate_solver([self])

        context = get_context(self)
        if context:
            context(partial(self._model._populate_solver, [self]))
            context(partial(self.__imul__, 1.0 / coefficient))

        return self
Exemplo n.º 5
0
    def remove_reactions(self, reactions, remove_orphans=False):
        """Remove reactions from the model.

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

        Parameters
        ----------
        reactions : list
            A list with reactions (`cobra.Reaction`), or their id's, to remove

        remove_orphans : bool
            Remove orphaned genes and metabolites from the model as well

        """
        if isinstance(reactions, string_types) or hasattr(reactions, "id"):
            warn("need to pass in a list")
            reactions = [reactions]

        context = get_context(self)

        for reaction in reactions:

            # Make sure the reaction is in the model
            try:
                reaction = self.reactions[self.reactions.index(reaction)]
            except ValueError:
                warn('%s not in %s' % (reaction, self))

            else:
                forward = reaction.forward_variable
                reverse = reaction.reverse_variable

                if context:
                    context(partial(self._populate_solver, [reaction]))
                    context(partial(setattr, reaction, '_model', self))
                    context(partial(self.reactions.add, reaction))

                self.remove_cons_vars([forward, reverse])
                self.reactions.remove(reaction)
                reaction._model = None

                for met in reaction._metabolites:
                    if reaction in met._reaction:
                        met._reaction.remove(reaction)
                        if context:
                            context(partial(met._reaction.add, reaction))
                        if remove_orphans and len(met._reaction) == 0:
                            self.remove_metabolites(met)

                for gene in reaction._genes:
                    if reaction in gene._reaction:
                        gene._reaction.remove(reaction)
                        if context:
                            context(partial(gene._reaction.add, reaction))

                        if remove_orphans and len(gene._reaction) == 0:
                            self.genes.remove(gene)
                            if context:
                                context(partial(self.genes.add, gene))
Exemplo n.º 6
0
    def remove_reactions(self, reactions, remove_orphans=False):
        """Remove reactions from the model.

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

        Parameters
        ----------
        reactions : list
            A list with reactions (`cobra.Reaction`), or their id's, to remove

        remove_orphans : bool
            Remove orphaned genes and metabolites from the model as well

        """
        if isinstance(reactions, string_types) or hasattr(reactions, "id"):
            warn("need to pass in a list")
            reactions = [reactions]

        context = get_context(self)

        for reaction in reactions:

            # Make sure the reaction is in the model
            try:
                reaction = self.reactions[self.reactions.index(reaction)]
            except ValueError:
                warn('%s not in %s' % (reaction, self))

            else:
                forward = reaction.forward_variable
                reverse = reaction.reverse_variable

                if context:
                    context(partial(self._populate_solver, [reaction]))
                    context(partial(setattr, reaction, '_model', self))
                    context(partial(self.reactions.add, reaction))

                self.remove_cons_vars([forward, reverse])
                self.reactions.remove(reaction)
                reaction._model = None

                for met in reaction._metabolites:
                    if reaction in met._reaction:
                        met._reaction.remove(reaction)
                        if context:
                            context(partial(met._reaction.add, reaction))
                        if remove_orphans and len(met._reaction) == 0:
                            self.remove_metabolites(met)

                for gene in reaction._genes:
                    if reaction in gene._reaction:
                        gene._reaction.remove(reaction)
                        if context:
                            context(partial(gene._reaction.add, reaction))

                        if remove_orphans and len(gene._reaction) == 0:
                            self.genes.remove(gene)
                            if context:
                                context(partial(self.genes.add, gene))
Exemplo n.º 7
0
    def gene_reaction_rule(self, new_rule):

        # TODO: Do this :)
        if get_context(self):
            warn("Context management not implemented for "
                 "gene reaction rules")

        self._gene_reaction_rule = new_rule.strip()
        try:
            _, gene_names = parse_gpr(self._gene_reaction_rule)
        except (SyntaxError, TypeError) as e:
            if "AND" in new_rule or "OR" in new_rule:
                warn("uppercase AND/OR found in rule '%s' for '%s'" %
                     (new_rule, repr(self)))
                new_rule = uppercase_AND.sub("and", new_rule)
                new_rule = uppercase_OR.sub("or", new_rule)
                self.gene_reaction_rule = new_rule
                return
            warn("malformed gene_reaction_rule '%s' for %s" %
                 (new_rule, repr(self)))
            tmp_str = and_or_search.sub("", self._gene_reaction_rule)
            gene_names = set((gpr_clean.sub(" ", tmp_str).split(" ")))
        if "" in gene_names:
            gene_names.remove("")
        old_genes = self._genes
        if self._model is None:
            self._genes = {Gene(i) for i in gene_names}
        else:
            model_genes = self._model.genes
            self._genes = set()
            for id in gene_names:
                if model_genes.has_id(id):
                    self._genes.add(model_genes.get_by_id(id))
                else:
                    new_gene = Gene(id)
                    new_gene._model = self._model
                    self._genes.add(new_gene)
                    model_genes.append(new_gene)

        # Make the genes aware that it is involved in this reaction
        for g in self._genes:
            g._reaction.add(self)

        # make the old genes aware they are no longer involved in this reaction
        for g in old_genes:
            if g not in self._genes:  # if an old gene is not a new gene
                try:
                    g._reaction.remove(self)
                except KeyError:
                    warn("could not remove old gene %s from reaction %s" %
                         (g.id, self.id))
Exemplo n.º 8
0
    def gene_reaction_rule(self, new_rule):

        # TODO: Do this :)
        if get_context(self):
            warn("Context management not implemented for "
                 "gene reaction rules")

        self._gene_reaction_rule = new_rule.strip()
        try:
            _, gene_names = parse_gpr(self._gene_reaction_rule)
        except (SyntaxError, TypeError) as e:
            if "AND" in new_rule or "OR" in new_rule:
                warn("uppercase AND/OR found in rule '%s' for '%s'" %
                     (new_rule, repr(self)))
                new_rule = uppercase_AND.sub("and", new_rule)
                new_rule = uppercase_OR.sub("or", new_rule)
                self.gene_reaction_rule = new_rule
                return
            warn("malformed gene_reaction_rule '%s' for %s" %
                 (new_rule, repr(self)))
            tmp_str = and_or_search.sub('', self._gene_reaction_rule)
            gene_names = set((gpr_clean.sub(' ', tmp_str).split(' ')))
        if '' in gene_names:
            gene_names.remove('')
        old_genes = self._genes
        if self._model is None:
            self._genes = {Gene(i) for i in gene_names}
        else:
            model_genes = self._model.genes
            self._genes = set()
            for id in gene_names:
                if model_genes.has_id(id):
                    self._genes.add(model_genes.get_by_id(id))
                else:
                    new_gene = Gene(id)
                    new_gene._model = self._model
                    self._genes.add(new_gene)
                    model_genes.append(new_gene)

        # Make the genes aware that it is involved in this reaction
        for g in self._genes:
            g._reaction.add(self)

        # make the old genes aware they are no longer involved in this reaction
        for g in old_genes:
            if g not in self._genes:  # if an old gene is not a new gene
                try:
                    g._reaction.remove(self)
                except:
                    warn("could not remove old gene %s from reaction %s" %
                         (g.id, self.id))
Exemplo n.º 9
0
    def add_metabolites(self, metabolite_list):
        """Will add a list of metabolites to the model object and add new
        constraints accordingly.

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

        Parameters
        ----------
        metabolite_list : A list of `cobra.core.Metabolite` objects

        """
        if not hasattr(metabolite_list, "__iter__"):
            metabolite_list = [metabolite_list]
        if len(metabolite_list) == 0:
            return None

        # First check whether the metabolites exist in the model
        metabolite_list = [
            x for x in metabolite_list if x.id not in self.metabolites
        ]

        bad_ids = [
            m for m in metabolite_list
            if not isinstance(m.id, string_types) or len(m.id) < 1
        ]
        if len(bad_ids) != 0:
            raise ValueError("invalid identifiers in {}".format(repr(bad_ids)))

        for x in metabolite_list:
            x._model = self
        self.metabolites += metabolite_list

        # from cameo ...
        to_add = []
        for met in metabolite_list:
            if met.id not in self.constraints:
                constraint = self.problem.Constraint(Zero,
                                                     name=met.id,
                                                     lb=0,
                                                     ub=0)
                to_add += [constraint]

        self.add_cons_vars(to_add)

        context = get_context(self)
        if context:
            context(partial(self.metabolites.__isub__, metabolite_list))
            for x in metabolite_list:
                # Do we care?
                context(partial(setattr, x, "_model", None))
Exemplo n.º 10
0
    def remove_metabolites(self, metabolite_list, destructive=False):
        """Remove a list of metabolites from the the object.

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

        Parameters
        ----------
        metabolite_list : list
            A list with `cobra.Metabolite` objects as elements.

        destructive : bool
            If False then the metabolite is removed from all
            associated reactions.  If True then all associated
            reactions are removed from the Model.

        """
        if not hasattr(metabolite_list, "__iter__"):
            metabolite_list = [metabolite_list]
        # Make sure metabolites exist in model
        metabolite_list = [
            x for x in metabolite_list if x.id in self.metabolites
        ]
        for x in metabolite_list:
            x._model = None

            # remove reference to the metabolite in all groups
            associated_groups = self.get_associated_groups(x)
            for group in associated_groups:
                group.remove_members(x)

            if not destructive:
                for the_reaction in list(x._reaction):
                    the_coefficient = the_reaction._metabolites[x]
                    the_reaction.subtract_metabolites({x: the_coefficient})

            else:
                for x in list(x._reaction):
                    x.remove_from_model()

        self.metabolites -= metabolite_list

        to_remove = [self.solver.constraints[m.id] for m in metabolite_list]
        self.remove_cons_vars(to_remove)

        context = get_context(self)
        if context:
            context(partial(self.metabolites.__iadd__, metabolite_list))
            for x in metabolite_list:
                context(partial(setattr, x, "_model", self))
Exemplo n.º 11
0
    def add_metabolites(self, metabolite_list):
        """Will add a list of metabolites to the model object and add new
        constraints accordingly.

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

        Parameters
        ----------
        metabolite_list : A list of `cobra.core.Metabolite` objects

        """
        if not hasattr(metabolite_list, '__iter__'):
            metabolite_list = [metabolite_list]
        if len(metabolite_list) == 0:
            return None

        # First check whether the metabolites exist in the model
        metabolite_list = [x for x in metabolite_list
                           if x.id not in self.metabolites]

        bad_ids = [m for m in metabolite_list
                   if not isinstance(m.id, string_types) or len(m.id) < 1]
        if len(bad_ids) != 0:
            raise ValueError('invalid identifiers in {}'.format(repr(bad_ids)))

        for x in metabolite_list:
            x._model = self
        self.metabolites += metabolite_list

        # from cameo ...
        to_add = []
        for met in metabolite_list:
            if met.id not in self.constraints:
                constraint = self.problem.Constraint(
                    Zero, name=met.id, lb=0, ub=0)
                to_add += [constraint]

        self.add_cons_vars(to_add)

        context = get_context(self)
        if context:
            context(partial(self.metabolites.__isub__, metabolite_list))
            for x in metabolite_list:
                # Do we care?
                context(partial(setattr, x, '_model', None))
Exemplo n.º 12
0
    def remove_metabolites(self, metabolite_list, destructive=False):
        """Remove a list of metabolites from the the object.

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

        Parameters
        ----------
        metabolite_list : list
            A list with `cobra.Metabolite` objects as elements.

        destructive : bool
            If False then the metabolite is removed from all
            associated reactions.  If True then all associated
            reactions are removed from the Model.

        """
        if not hasattr(metabolite_list, '__iter__'):
            metabolite_list = [metabolite_list]
        # Make sure metabolites exist in model
        metabolite_list = [x for x in metabolite_list
                           if x.id in self.metabolites]
        for x in metabolite_list:
            x._model = None

            if not destructive:
                for the_reaction in list(x._reaction):
                    the_coefficient = the_reaction._metabolites[x]
                    the_reaction.subtract_metabolites({x: the_coefficient})

            else:
                for x in list(x._reaction):
                    x.remove_from_model()

        self.metabolites -= metabolite_list

        to_remove = [self.solver.constraints[m.id] for m in metabolite_list]
        self.remove_cons_vars(to_remove)

        context = get_context(self)
        if context:
            context(partial(self.metabolites.__iadd__, metabolite_list))
            for x in metabolite_list:
                context(partial(setattr, x, '_model', self))
Exemplo n.º 13
0
    def add_metabolites(self, metabolite_list):
        """Will add a list of metabolites to the model object and add new
        constraints accordingly.

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

        Parameters
        ----------
        metabolite_list : A list of `cobra.core.Metabolite` objects

        """
        if not hasattr(metabolite_list, '__iter__'):
            metabolite_list = [metabolite_list]
        if len(metabolite_list) == 0:
            return None

        # First check whether the metabolites exist in the model
        metabolite_list = [
            x for x in metabolite_list if x.id not in self.metabolites
        ]
        for x in metabolite_list:
            x._model = self
        self.metabolites += metabolite_list

        # from cameo ...
        to_add = []
        for met in metabolite_list:
            if met.id not in self.constraints:
                constraint = self.problem.Constraint(S.Zero,
                                                     name=met.id,
                                                     lb=0,
                                                     ub=0)
                to_add += [constraint]

        self.add_cons_vars(to_add)

        context = get_context(self)
        if context:
            context(partial(self.metabolites.__isub__, metabolite_list))
            for x in metabolite_list:
                # Do we care?
                context(partial(setattr, x, '_model', None))
Exemplo n.º 14
0
def remove_cons_vars_from_problem(model, what):
    """Remove variables and constraints from a Model's solver object.

    Useful to temporarily remove variables and constraints from a Models's
    solver object.

    Parameters
    ----------
    model : a cobra model
       The model from which to remove the variables and constraints.
    what : list or tuple of optlang variables or constraints.
       The variables or constraints to remove from the model. Must be of
       class `model.problem.Variable` or
       `model.problem.Constraint`.
    """
    context = get_context(model)

    model.solver.remove(what)
    if context:
        context(partial(model.solver.add, what))
Exemplo n.º 15
0
def remove_cons_vars_from_problem(model, what):
    """Remove variables and constraints from a Model's solver object.

    Useful to temporarily remove variables and constraints from a Models's
    solver object.

    Parameters
    ----------
    model : a cobra model
       The model from which to remove the variables and constraints.
    what : list or tuple of optlang variables or constraints.
       The variables or constraints to remove from the model. Must be of
       class `model.problem.Variable` or
       `model.problem.Constraint`.
    """
    context = get_context(model)

    model.solver.remove(what)
    if context:
        context(partial(model.solver.add, what))
Exemplo n.º 16
0
def _apply_min_growth(community, min_growth):
    """Set minimum growth constraints on a model.

    Will integrate with the context.
    """
    context = get_context(community)

    def reset(species, lb):
        logger.info("resetting growth rate constraint for %s" % species)
        community.constraints["objective_" + species].ub = None
        community.constraints["objective_" + species].lb = lb

    for sp in community.species:
        logger.info("setting growth rate constraint for %s" % sp)
        obj = community.constraints["objective_" + sp]
        if context:
            context(partial(reset, sp, obj.lb))
        if min_growth[sp] > 1e-6:
            obj.lb = min_growth[sp]
        else:
            logger.info("minimal growth rate smaller than tolerance,"
                        " setting to zero.")
            obj.lb = 0
Exemplo n.º 17
0
def remove_cons_vars_from_problem(
    model: "Model",
    what: Union[List[CONS_VARS], Tuple[CONS_VARS], Components],
) -> None:
    """Remove variables and constraints from a model's solver object.

    Useful to temporarily remove variables and constraints from a model's
    solver object.

    Parameters
    ----------
    model : cobra.Model
       The model from which to remove the variables and constraints.
    what : list or tuple of optlang.interface.Variable or
           optlang.interface.Constraint
       The variables and constraints to remove from the model.

    """
    model.solver.remove(what)

    context = get_context(model)
    if context:
        context(partial(model.solver.add, what))
Exemplo n.º 18
0
def add_cons_vars_to_problem(model, what, **kwargs):
    """Add variables and constraints to a Model's solver object.

    Useful for variables and constraints that can not be expressed with
    reactions and lower/upper bounds. Will integrate with the Model's context
    manager in order to revert changes upon leaving the context.

    Parameters
    ----------
    model : a cobra model
       The model to which to add the variables and constraints.
    what : list or tuple of optlang variables or constraints.
       The variables or constraints to add to the model. Must be of class
       `model.problem.Variable` or
       `model.problem.Constraint`.
    **kwargs : keyword arguments
        passed to solver.add()
    """
    context = get_context(model)

    model.solver.add(what, **kwargs)
    if context:
        context(partial(model.solver.remove, what))
Exemplo n.º 19
0
def add_cons_vars_to_problem(model, what, **kwargs):
    """Add variables and constraints to a Model's solver object.

    Useful for variables and constraints that can not be expressed with
    reactions and lower/upper bounds. Will integrate with the Model's context
    manager in order to revert changes upon leaving the context.

    Parameters
    ----------
    model : a cobra model
       The model to which to add the variables and constraints.
    what : list or tuple of optlang variables or constraints.
       The variables or constraints to add to the model. Must be of class
       `model.problem.Variable` or
       `model.problem.Constraint`.
    **kwargs : keyword arguments
        passed to solver.add()
    """
    context = get_context(model)

    model.solver.add(what, **kwargs)
    if context:
        context(partial(model.solver.remove, what))
Exemplo n.º 20
0
    def __imul__(self, coefficient):
        """Scale coefficients in a reaction by a given value

        E.g. A -> B becomes 2A -> 2B.

        If coefficient is less than zero, the reaction is reversed and the
        bounds are swapped.
        """
        self._metabolites = {
            met: value * coefficient
            for met, value in iteritems(self._metabolites)}

        if coefficient < 0:
            self.bounds = (-self.upper_bound, -self.lower_bound)

        if self._model:
            self._model._populate_solver([self])

        context = get_context(self)
        if context:
            context(partial(self._model._populate_solver, [self]))
            context(partial(self.__imul__, 1./coefficient))

        return self
Exemplo n.º 21
0
    def add_reactions(self, reaction_list):
        """Add reactions to the model.

        Reactions with identifiers identical to a reaction already in the
        model are ignored.

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

        Parameters
        ----------
        reaction_list : list
            A list of `cobra.Reaction` objects
        """
        def existing_filter(rxn):
            if rxn.id in self.reactions:
                LOGGER.warning(
                    "Ignoring reaction '%s' since it already exists.", rxn.id)
                return False
            return True

        # First check whether the reactions exist in the model.
        pruned = DictList(filter(existing_filter, reaction_list))

        context = get_context(self)

        # Add reactions. Also take care of genes and metabolites in the loop.
        for reaction in pruned:
            reaction._model = self
            # Build a `list()` because the dict will be modified in the loop.
            for metabolite in list(reaction.metabolites):
                # TODO: Should we add a copy of the metabolite instead?
                if metabolite not in self.metabolites:
                    self.add_metabolites(metabolite)
                # A copy of the metabolite exists in the model, the reaction
                # needs to point to the metabolite in the model.
                else:
                    # FIXME: Modifying 'private' attributes is horrible.
                    stoichiometry = reaction._metabolites.pop(metabolite)
                    model_metabolite = self.metabolites.get_by_id(
                        metabolite.id)
                    reaction._metabolites[model_metabolite] = stoichiometry
                    model_metabolite._reaction.add(reaction)
                    if context:
                        context(partial(
                            model_metabolite._reaction.remove, reaction))

            for gene in list(reaction._genes):
                # If the gene is not in the model, add it
                if not self.genes.has_id(gene.id):
                    self.genes += [gene]
                    gene._model = self

                    if context:
                        # Remove the gene later
                        context(partial(self.genes.__isub__, [gene]))
                        context(partial(setattr, gene, '_model', None))

                # Otherwise, make the gene point to the one in the model
                else:
                    model_gene = self.genes.get_by_id(gene.id)
                    if model_gene is not gene:
                        reaction._dissociate_gene(gene)
                        reaction._associate_gene(model_gene)

        self.reactions += pruned

        if context:
            context(partial(self.reactions.__isub__, pruned))

        # from cameo ...
        self._populate_solver(pruned)
Exemplo n.º 22
0
    def add_reactions(self, reaction_list: Iterator[Reaction]):
        """Add reactions to the model.

        Reactions with identifiers identical to a reaction already in the
        model are ignored.
        The change is reverted upon exit when using the model as a context.
        Enzyme Constrained changes: avoid adding proteins as metabolites.

        Parameters
        ----------
        reaction_list : list
            A list of `cobra.Reaction` objects
        """
        def existing_filter(rxn):
            if rxn.id in self.reactions:
                LOGGER.warning(
                    f"Ignoring reaction '{rxn.id}' since it already exists.")
                return False
            return True

        # First check whether the reactions exist in the model.
        pruned = DictList(filter(existing_filter, reaction_list))

        context = get_context(self)

        # Add reactions. Also take care of genes and metabolites in the loop.
        for reaction in pruned:
            reaction._model = self
            # Build a `list()` because the dict will be modified in the loop.
            for metabolite in list(reaction.metabolites):
                is_prot = isinstance(metabolite, Protein)
                target = self.proteins if is_prot else self.metabolites
                if metabolite not in target:
                    if is_prot:
                        self.add_proteins([metabolite])
                    else:
                        self.add_metabolites(metabolite)

                # A copy of the metabolite exists in the model, the reaction
                # needs to point to the metabolite in the model.
                else:
                    stoichiometry = reaction._metabolites.pop(metabolite)

                    model_metabolite = target.get_by_id(metabolite.id)
                    reaction._metabolites[model_metabolite] = stoichiometry
                    model_metabolite._reaction.add(reaction)
                    if context:
                        context(
                            partial(model_metabolite._reaction.remove,
                                    reaction))

            for gene in list(reaction._genes):
                # If the gene is not in the model, add it
                if not self.genes.has_id(gene.id):
                    self.genes += [gene]
                    gene._model = self

                    if context:
                        # Remove the gene later
                        context(partial(self.genes.__isub__, [gene]))
                        context(partial(setattr, gene, "_model", None))

                # Otherwise, make the gene point to the one in the model
                else:
                    model_gene = self.genes.get_by_id(gene.id)
                    if model_gene is not gene:
                        reaction._dissociate_gene(gene)
                        reaction._associate_gene(model_gene)

        self.reactions += pruned

        if context:
            context(partial(self.reactions.__isub__, pruned))

        # from cameo ...
        self._populate_solver(pruned)
Exemplo n.º 23
0
def set_objective(model, value, additive=False):
    """Set the model objective.

    Parameters
    ----------
    model : cobra model
       The model to set the objective for
    value : model.problem.Objective,
            e.g. optlang.glpk_interface.Objective, sympy.Basic or dict

        If the model objective is linear, the value can be a new Objective
        object or a dictionary with linear coefficients where each key is a
        reaction and the element the new coefficient (float).

        If the objective is not linear and `additive` is true, only values
        of class Objective.

    additive : boolmodel.reactions.Biomass_Ecoli_core.bounds = (0.1, 0.1)
        If true, add the terms to the current objective, otherwise start with
        an empty objective.
    """
    interface = model.problem
    reverse_value = model.solver.objective.expression
    reverse_value = interface.Objective(
        reverse_value, direction=model.solver.objective.direction,
        sloppy=True)

    if isinstance(value, dict):
        if not model.objective.is_Linear:
            raise ValueError('can only update non-linear objectives '
                             'additively using object of class '
                             'model.problem.Objective, not %s' %
                             type(value))

        if not additive:
            model.solver.objective = interface.Objective(
                Zero, direction=model.solver.objective.direction)
        for reaction, coef in value.items():
            model.solver.objective.set_linear_coefficients(
                {reaction.forward_variable: coef,
                 reaction.reverse_variable: -coef})

    elif isinstance(value, (Basic, optlang.interface.Objective)):
        if isinstance(value, Basic):
            value = interface.Objective(
                value, direction=model.solver.objective.direction,
                sloppy=False)
        # Check whether expression only uses variables from current model
        # clone the objective if not, faster than cloning without checking
        if not _valid_atoms(model, value.expression):
            value = interface.Objective.clone(value, model=model.solver)

        if not additive:
            model.solver.objective = value
        else:
            model.solver.objective += value.expression
    else:
        raise TypeError(
            '%r is not a valid objective for %r.' % (value, model.solver))

    context = get_context(model)
    if context:
        def reset():
            model.solver.objective = reverse_value
            model.solver.objective.direction = reverse_value.direction

        context(reset)
Exemplo n.º 24
0
    def remove_reactions(self, reactions, delete=True, remove_orphans=False):
        """Remove reactions from the model.

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

        Parameters
        ----------
        reactions : list
            A list with reactions (`cobra.Reaction`), or their id's, to remove

        delete : bool
            Whether or not the reactions should be deleted after removal.
            If the reactions are not deleted, those objects will be
            recreated with new metabolite and gene objects.

        remove_orphans : bool
            Remove orphaned genes and metabolites from the model as well

        """
        if isinstance(reactions, string_types) or hasattr(reactions, "id"):
            warn("need to pass in a list")
            reactions = [reactions]

        context = get_context(self)

        for reaction in reactions:
            try:
                reaction = self.reactions[self.reactions.index(reaction)]
            except ValueError:
                warn('%s not in %s' % (reaction, self))
            else:
                forward = reaction.forward_variable
                reverse = reaction.reverse_variable
                self.remove_cons_vars([forward, reverse])
                self.reactions.remove(reaction)
                reaction._model = None

                if context:
                    context(partial(setattr, reaction, '_model', self))
                    context(partial(self.reactions.add, reaction))

                for x in reaction._metabolites:
                    if reaction in x._reaction:
                        x._reaction.remove(reaction)
                        if context:
                            context(partial(x._reaction.add, reaction))
                        if remove_orphans and len(x._reaction) == 0:
                            self.remove_metabolites(x)

                for x in reaction._genes:
                    if reaction in x._reaction:
                        x._reaction.remove(reaction)
                        if context:
                            context(partial(x._reaction.add, reaction))

                        if remove_orphans and len(x._reaction) == 0:
                            self.genes.remove(x)
                            if context:
                                context(partial(self.genes.add, x))

                reaction._metabolites = {}
                reaction._genes = set()
Exemplo n.º 25
0
    def add_reactions(self, reaction_list):
        """Will add a cobra.Reaction object to the model, if
        reaction.id is not in self.reactions.

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

        Parameters
        ----------
        reaction_list : list
            A list of `cobra.Reaction` objects

        """

        try:
            reaction_list = DictList(reaction_list)
        except TypeError:
            reaction_list = DictList([reaction_list])

        # Only add the reaction if one with the same ID is not already
        # present in the model.
        reactions_in_model = [
            i.id for i in reaction_list if i.id in self.reactions
        ]

        if len(reactions_in_model) > 0:
            raise Exception("Reactions already in the model: " +
                            ", ".join(reactions_in_model))

        context = get_context(self)

        # Add reactions. Also take care of genes and metabolites in the loop
        for reaction in reaction_list:
            reaction._reset_var_cache()
            reaction._model = self  # the reaction now points to the model
            # keys() is necessary because the dict will be modified during
            # the loop
            for metabolite in list(reaction._metabolites.keys()):
                # if the metabolite is not in the model, add it
                # should we be adding a copy instead.
                if metabolite not in self.metabolites:
                    self.add_metabolites(metabolite)
                # A copy of the metabolite exists in the model, the reaction
                # needs to point to the metabolite in the model.
                else:
                    stoichiometry = reaction._metabolites.pop(metabolite)
                    model_metabolite = self.metabolites.get_by_id(
                        metabolite.id)
                    reaction._metabolites[model_metabolite] = stoichiometry
                    model_metabolite._reaction.add(reaction)
                    if context:
                        context(
                            partial(model_metabolite._reaction.remove,
                                    reaction))

            for gene in list(reaction._genes):
                # If the gene is not in the model, add it
                if not self.genes.has_id(gene.id):
                    self.genes += [gene]
                    gene._model = self

                    if context:
                        # Remove the gene later
                        context(partial(self.genes.__isub__, [gene]))
                        context(partial(setattr, gene, '_model', None))

                # Otherwise, make the gene point to the one in the model
                else:
                    model_gene = self.genes.get_by_id(gene.id)
                    if model_gene is not gene:
                        reaction._dissociate_gene(gene)
                        reaction._associate_gene(model_gene)

        self.reactions += reaction_list

        if context:
            context(partial(self.reactions.__isub__, reaction_list))

        # from cameo ...
        self._populate_solver(reaction_list)
Exemplo n.º 26
0
    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.


        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):
            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" % self.id)
                self._metabolites[metabolite] = coefficient
                # make the metabolite aware that it is involved in this
                # reaction
                metabolite._reaction.add(self)

        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)

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

            for metabolite, coefficient in metabolites_to_add.items():

                if isinstance(metabolite,
                              str):  # support metabolites added as strings.
                    metabolite = model.metabolites.get_by_id(metabolite)
                if combine:
                    try:
                        old_coefficient = old_coefficients[metabolite]
                    except KeyError:
                        pass
                    else:
                        coefficient = coefficient + old_coefficient

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

        context = get_context(self)
        if context and reversibly:
            # Just subtract the metabolites that were added
            context(
                partial(self.subtract_metabolites,
                        metabolites_to_add,
                        combine=True,
                        reversibly=False))
Exemplo n.º 27
0
    def add_reactions(self, reaction_list):
        """Add reactions to the model.

        Reactions with identifiers identical to a reaction already in the
        model are ignored.

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

        Parameters
        ----------
        reaction_list : list
            A list of `cobra.Reaction` objects
        """

        try:
            reaction_list = DictList(reaction_list)
        except TypeError:
            reaction_list = DictList([reaction_list])

        # First check whether the metabolites exist in the model
        existing = [rxn for rxn in reaction_list if rxn.id in self.reactions]
        for rxn in existing:
            LOGGER.info('skip adding reaction %s as already existing', rxn.id)
        reaction_list = [
            rxn for rxn in reaction_list if rxn.id not in existing
        ]

        context = get_context(self)

        # Add reactions. Also take care of genes and metabolites in the loop
        for reaction in reaction_list:
            reaction._reset_var_cache()
            reaction._model = self  # the reaction now points to the model
            # keys() is necessary because the dict will be modified during
            # the loop
            for metabolite in list(reaction._metabolites.keys()):
                # if the metabolite is not in the model, add it
                # should we be adding a copy instead.
                if metabolite not in self.metabolites:
                    self.add_metabolites(metabolite)
                # A copy of the metabolite exists in the model, the reaction
                # needs to point to the metabolite in the model.
                else:
                    stoichiometry = reaction._metabolites.pop(metabolite)
                    model_metabolite = self.metabolites.get_by_id(
                        metabolite.id)
                    reaction._metabolites[model_metabolite] = stoichiometry
                    model_metabolite._reaction.add(reaction)
                    if context:
                        context(
                            partial(model_metabolite._reaction.remove,
                                    reaction))

            for gene in list(reaction._genes):
                # If the gene is not in the model, add it
                if not self.genes.has_id(gene.id):
                    self.genes += [gene]
                    gene._model = self

                    if context:
                        # Remove the gene later
                        context(partial(self.genes.__isub__, [gene]))
                        context(partial(setattr, gene, '_model', None))

                # Otherwise, make the gene point to the one in the model
                else:
                    model_gene = self.genes.get_by_id(gene.id)
                    if model_gene is not gene:
                        reaction._dissociate_gene(gene)
                        reaction._associate_gene(model_gene)

        self.reactions += reaction_list

        if context:
            context(partial(self.reactions.__isub__, reaction_list))

        # from cameo ...
        self._populate_solver(reaction_list)
Exemplo n.º 28
0
def set_objective(model, value, additive=False):
    """Set the model objective.

    Parameters
    ----------
    model : cobra model
       The model to set the objective for
    value : model.problem.Objective,
            e.g. optlang.glpk_interface.Objective, sympy.Basic or dict

        If the model objective is linear, the value can be a new Objective
        object or a dictionary with linear coefficients where each key is a
        reaction and the element the new coefficient (float).

        If the objective is not linear and `additive` is true, only values
        of class Objective.

    additive : boolmodel.reactions.Biomass_Ecoli_core.bounds = (0.1, 0.1)
        If true, add the terms to the current objective, otherwise start with
        an empty objective.
    """
    interface = model.problem
    reverse_value = model.solver.objective.expression
    reverse_value = interface.Objective(
        reverse_value, direction=model.solver.objective.direction,
        sloppy=True)

    if isinstance(value, dict):
        if not model.objective.is_Linear:
            raise ValueError('can only update non-linear objectives '
                             'additively using object of class '
                             'model.problem.Objective, not %s' %
                             type(value))

        if not additive:
            model.solver.objective = interface.Objective(
                Zero, direction=model.solver.objective.direction)
        for reaction, coef in value.items():
            model.solver.objective.set_linear_coefficients(
                {reaction.forward_variable: coef,
                 reaction.reverse_variable: -coef})

    elif isinstance(value, (Basic, optlang.interface.Objective)):
        if isinstance(value, Basic):
            value = interface.Objective(
                value, direction=model.solver.objective.direction,
                sloppy=False)
        # Check whether expression only uses variables from current model
        # clone the objective if not, faster than cloning without checking
        if not _valid_atoms(model, value.expression):
            value = interface.Objective.clone(value, model=model.solver)

        if not additive:
            model.solver.objective = value
        else:
            model.solver.objective += value.expression
    else:
        raise TypeError(
            '%r is not a valid objective for %r.' % (value, model.solver))

    context = get_context(model)
    if context:
        def reset():
            model.solver.objective = reverse_value
            model.solver.objective.direction = reverse_value.direction

        context(reset)
Exemplo n.º 29
0
def set_objective(
    model: "Model",
    value: Union[optlang.interface.Objective, optlang.symbolics.Basic,
                 Dict["Reaction", float], ],
    additive: bool = False,
) -> None:
    """Set the model objective.

    Parameters
    ----------
    model : cobra.Model
       The model to set the objective for.
    value : optlang.interface.Objective, optlang.symbolics.Basic, dict
        If the model objective is linear, then the value can be a new
        optlang.interface.Objective or a dictionary with linear
        coefficients where each key is a reaction and the corresponding
        value is the new coefficient (float).
        If the objective is non-linear and `additive` is True, then only
        values of class optlang.interface.Objective, are accepted.
    additive : bool
        If True, add the terms to the current objective, otherwise start with
        an empty objective.

    Raises
    ------
    ValueError
        If model objective is non-linear and the `value` is a dict.
    TypeError
        If the type of `value` is not one of the accepted ones.

    """
    interface = model.problem
    reverse_value = model.solver.objective.expression
    reverse_value = interface.Objective(
        reverse_value, direction=model.solver.objective.direction, sloppy=True)

    if isinstance(value, dict):
        if not model.objective.is_Linear:
            raise ValueError(
                "You can only update non-linear objectives additively using object of "
                f"class optlang.interface.Objective, not of {type(value)}")

        if not additive:
            model.solver.objective = interface.Objective(
                Zero, direction=model.solver.objective.direction)
        for reaction, coef in value.items():
            model.solver.objective.set_linear_coefficients({
                reaction.forward_variable:
                coef,
                reaction.reverse_variable:
                -coef
            })

    elif isinstance(value, (Basic, optlang.interface.Objective)):
        if isinstance(value, Basic):
            value = interface.Objective(
                value,
                direction=model.solver.objective.direction,
                sloppy=False)
        # Check whether expression only uses variables from current model;
        # clone the objective if not, faster than cloning without checking
        if not _valid_atoms(model, value.expression):
            value = interface.Objective.clone(value, model=model.solver)

        if not additive:
            model.solver.objective = value
        else:
            model.solver.objective += value.expression
    else:
        raise TypeError(
            f"{value} is not a valid objective for {model.solver}.")

    context = get_context(model)
    if context:

        def reset():
            model.solver.objective = reverse_value
            model.solver.objective.direction = reverse_value.direction

        context(reset)
Exemplo n.º 30
0
    def remove_reactions(self, reactions, remove_orphans=False):
        """Remove reactions from the model.

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

        Parameters
        ----------
        reactions : list
            A list with reactions (`cobra.Reaction`), or their id's, to remove

        remove_orphans : bool
            Remove orphaned genes and metabolites from the model as well

        """
        if isinstance(reactions, string_types) or hasattr(reactions, "id"):
            warn("need to pass in a list")
            reactions = [reactions]

        context = get_context(self)

        for reaction in reactions:

            # Make sure the reaction is in the model
            try:
                reaction = self.reactions[self.reactions.index(reaction)]
            except ValueError:
                warn("%s not in %s" % (reaction, self))

            else:
                forward = reaction.forward_variable
                reverse = reaction.reverse_variable

                if context:

                    obj_coef = reaction.objective_coefficient

                    if obj_coef != 0:
                        context(
                            partial(
                                self.solver.objective.set_linear_coefficients,
                                {
                                    forward: obj_coef,
                                    reverse: -obj_coef
                                },
                            ))

                    context(partial(self._populate_solver, [reaction]))
                    context(partial(setattr, reaction, "_model", self))
                    context(partial(self.reactions.add, reaction))

                self.remove_cons_vars([forward, reverse])
                self.reactions.remove(reaction)
                reaction._model = None

                for met in reaction._metabolites:
                    if reaction in met._reaction:
                        met._reaction.remove(reaction)
                        if context:
                            context(partial(met._reaction.add, reaction))
                        if remove_orphans and len(met._reaction) == 0:
                            self.remove_metabolites(met)

                for gene in reaction._genes:
                    if reaction in gene._reaction:
                        gene._reaction.remove(reaction)
                        if context:
                            context(partial(gene._reaction.add, reaction))

                        if remove_orphans and len(gene._reaction) == 0:
                            self.genes.remove(gene)
                            if context:
                                context(partial(self.genes.add, gene))

                # remove reference to the reaction in all groups
                associated_groups = self.get_associated_groups(reaction)
                for group in associated_groups:
                    group.remove_members(reaction)
Exemplo n.º 31
0
    def add_reactions(self, reaction_list):
        """Add reactions to the model.

        Reactions with identifiers identical to a reaction already in the
        model are ignored.

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

        Parameters
        ----------
        reaction_list : list
            A list of `cobra.Reaction` objects
        """
        def existing_filter(rxn):
            if rxn.id in self.reactions:
                logger.warning(
                    "Ignoring reaction '%s' since it already exists.", rxn.id)
                return False
            return True

        # First check whether the reactions exist in the model.
        pruned = DictList(filter(existing_filter, reaction_list))

        context = get_context(self)

        # Add reactions. Also take care of genes and metabolites in the loop.
        for reaction in pruned:
            reaction._model = self
            # Build a `list()` because the dict will be modified in the loop.
            for metabolite in list(reaction.metabolites):
                # TODO: Should we add a copy of the metabolite instead?
                if metabolite not in self.metabolites:
                    self.add_metabolites(metabolite)
                # A copy of the metabolite exists in the model, the reaction
                # needs to point to the metabolite in the model.
                else:
                    # FIXME: Modifying 'private' attributes is horrible.
                    stoichiometry = reaction._metabolites.pop(metabolite)
                    model_metabolite = self.metabolites.get_by_id(
                        metabolite.id)
                    reaction._metabolites[model_metabolite] = stoichiometry
                    model_metabolite._reaction.add(reaction)
                    if context:
                        context(
                            partial(model_metabolite._reaction.remove,
                                    reaction))

            for gene in list(reaction._genes):
                # If the gene is not in the model, add it
                if not self.genes.has_id(gene.id):
                    self.genes += [gene]
                    gene._model = self

                    if context:
                        # Remove the gene later
                        context(partial(self.genes.__isub__, [gene]))
                        context(partial(setattr, gene, "_model", None))

                # Otherwise, make the gene point to the one in the model
                else:
                    model_gene = self.genes.get_by_id(gene.id)
                    if model_gene is not gene:
                        reaction._dissociate_gene(gene)
                        reaction._associate_gene(model_gene)

        self.reactions += pruned

        if context:
            context(partial(self.reactions.__isub__, pruned))
Exemplo n.º 32
0
    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 metabolites_to_add.items():

            # 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, str):
                    # 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 metabolites_to_add.keys()
                }

                context(
                    partial(
                        self.add_metabolites,
                        mets_to_reset,
                        combine=False,
                        reversibly=False,
                    ))
Exemplo n.º 33
0
    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):
            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)

        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)

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

            for metabolite, coefficient in metabolites_to_add.items():

                if isinstance(metabolite,
                              str):  # support metabolites added as strings.
                    metabolite = model.metabolites.get_by_id(metabolite)
                if combine:
                    try:
                        old_coefficient = old_coefficients[metabolite]
                    except KeyError:
                        pass
                    else:
                        coefficient = coefficient + old_coefficient

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

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