Example #1
0
def test_reaction_charge_balance(read_only_model):
    """
    Expect all reactions to be charge balanced.

    This will exclude biomass, exchange and demand reactions as they are
    unbalanced by definition. It will also fail all reactions where at
    least one metabolite does not have a charge defined.

    In steady state, for each metabolite the sum of influx equals the sum
    of outflux. Hence the net charges of both sides of any model reaction have
    to be equal. Reactions where at least one metabolite does not have a
    formula are not considered to be balanced, even though the remaining
    metabolites participating in the reaction might be.
    """
    ann = test_reaction_charge_balance.annotation
    internal_rxns = con_helpers.get_internals(read_only_model)
    ann["data"] = get_ids(
        consistency.find_charge_unbalanced_reactions(internal_rxns))
    ann["metric"] = len(ann["data"]) / len(internal_rxns)
    ann["message"] = wrapper.fill(
        """A total of {} ({:.2%}) reactions are charge unbalanced with at
        least one of the metabolites not having a charge or the overall
        charge not equal to 0: {}""".format(
            len(ann["data"]), ann["metric"], truncate(ann["data"])))
    assert len(ann["data"]) == 0, ann["message"]
Example #2
0
def test_reaction_mass_balance(model):
    """
    Expect all reactions to be mass balanced.

    This will exclude biomass, exchange and demand reactions as they are
    unbalanced by definition. It will also fail all reactions where at
    least one metabolite does not have a formula defined.

    In steady state, for each metabolite the sum of influx equals the sum
    of efflux. Hence the net masses of both sides of any model reaction have
    to be equal. Reactions where at least one metabolite does not have a
    formula are not considered to be balanced, even though the remaining
    metabolites participating in the reaction might be.

    Implementation:
    For each reaction that isn't a boundary or biomass reaction check if each
    metabolite has a non-zero elements attribute and if so calculate if the
    overall element balance of reactants and products is equal to zero.

    """
    ann = test_reaction_mass_balance.annotation
    internal_rxns = con_helpers.get_internals(model)
    ann["data"] = get_ids(
        consistency.find_mass_unbalanced_reactions(internal_rxns)
    )
    ann["metric"] = len(ann["data"]) / len(internal_rxns)
    ann["message"] = wrapper.fill(
        """A total of {} ({:.2%}) reactions are mass unbalanced with at least
        one of the metabolites not having a formula or the overall mass not
        equal to 0: {}""".format(
            len(ann["data"]), ann["metric"], truncate(ann["data"])))
    assert len(ann["data"]) == 0, ann["message"]
Example #3
0
def test_reaction_mass_balance(model):
    """
    Expect all reactions to be mass balanced.

    This will exclude biomass, exchange and demand reactions as they are
    unbalanced by definition. It will also fail all reactions where at
    least one metabolite does not have a formula defined.

    In steady state, for each metabolite the sum of influx equals the sum
    of efflux. Hence the net masses of both sides of any model reaction have
    to be equal. Reactions where at least one metabolite does not have a
    formula are not considered to be balanced, even though the remaining
    metabolites participating in the reaction might be.

    Implementation:
    For each reaction that isn't a boundary or biomass reaction check if each
    metabolite has a non-zero elements attribute and if so calculate if the
    overall element balance of reactants and products is equal to zero.

    """
    ann = test_reaction_mass_balance.annotation
    internal_rxns = con_helpers.get_internals(model)
    ann["data"] = get_ids(
        consistency.find_mass_unbalanced_reactions(internal_rxns))
    ann["metric"] = len(ann["data"]) / len(internal_rxns)
    ann["message"] = wrapper.fill(
        """A total of {} ({:.2%}) reactions are mass unbalanced with at least
        one of the metabolites not having a formula or the overall mass not
        equal to 0: {}""".format(len(ann["data"]), ann["metric"],
                                 truncate(ann["data"])))
    assert len(ann["data"]) == 0, ann["message"]
Example #4
0
def find_unconserved_metabolites(model):
    """
    Detect unconserved metabolites.

    Parameters
    ----------
    model : cobra.Model
        The metabolic model under investigation.

    Notes
    -----
    See [1]_ section 3.2 for a complete description of the algorithm.


    .. [1] Gevorgyan, A., M. G Poolman, and D. A Fell.
           "Detection of Stoichiometric Inconsistencies in Biomolecular
           Models."
           Bioinformatics 24, no. 19 (2008): 2245.

    """
    problem = model.problem
    stoich_trans = problem.Model()
    internal_rxns = con_helpers.get_internals(model)
    metabolites = set(met for rxn in internal_rxns for met in rxn.metabolites)
    # The binary variables k[i] in the paper.
    k_vars = list()
    for met in metabolites:
        # The element m[i] of the mass vector.
        m_var = problem.Variable(met.id)
        k_var = problem.Variable("k_{}".format(met.id), type="binary")
        k_vars.append(k_var)
        stoich_trans.add([m_var, k_var])
        # This constraint is equivalent to 0 <= k[i] <= m[i].
        stoich_trans.add(
            problem.Constraint(k_var - m_var,
                               ub=0,
                               name="switch_{}".format(met.id)))
    stoich_trans.update()
    con_helpers.add_reaction_constraints(stoich_trans, internal_rxns,
                                         problem.Constraint)
    # The objective is to maximize the binary indicators k[i], subject to the
    # above inequality constraints.
    stoich_trans.objective = problem.Objective(Zero,
                                               sloppy=True,
                                               direction="max")
    stoich_trans.objective.set_linear_coefficients({var: 1. for var in k_vars})
    status = stoich_trans.optimize()
    if status == OPTIMAL:
        # TODO: See if that could be a Boolean test `bool(var.primal)`.
        return set([
            model.metabolites.get_by_id(var.name[2:]) for var in k_vars
            if var.primal < 0.8
        ])
    else:
        raise RuntimeError(
            "Could not compute list of unconserved metabolites."
            " Solver status is '{}' (only optimal expected).".format(status))
Example #5
0
def check_stoichiometric_consistency(model):
    """
    Verify the consistency of the model's stoichiometry.

    Parameters
    ----------
    model : cobra.Model
        The metabolic model under investigation.

    Notes
    -----
    See [1]_ section 3.1 for a complete description of the algorithm.

    .. [1] Gevorgyan, A., M. G Poolman, and D. A Fell.
           "Detection of Stoichiometric Inconsistencies in Biomolecular
           Models."
           Bioinformatics 24, no. 19 (2008): 2245.

    """
    problem = model.problem
    # The transpose of the stoichiometric matrix N.T in the paper.
    stoich_trans = problem.Model()
    # We clone the existing configuration in order to apply non-default
    # settings, for example, for solver verbosity or timeout.
    stoich_trans.configuration = problem.Configuration.clone(
        config=model.solver.configuration, problem=stoich_trans)
    internal_rxns = con_helpers.get_internals(model)
    metabolites = set(met for rxn in internal_rxns for met in rxn.metabolites)
    LOGGER.info("model '%s' has %d internal reactions", model.id,
                len(internal_rxns))
    LOGGER.info("model '%s' has %d internal metabolites", model.id,
                len(metabolites))

    stoich_trans.add([problem.Variable(m.id, lb=1) for m in metabolites])
    stoich_trans.update()
    con_helpers.add_reaction_constraints(stoich_trans, internal_rxns,
                                         problem.Constraint)
    # The objective is to minimize the metabolite mass vector.
    stoich_trans.objective = problem.Objective(Zero,
                                               direction="min",
                                               sloppy=True)
    stoich_trans.objective.set_linear_coefficients(
        {var: 1.
         for var in stoich_trans.variables})
    status = stoich_trans.optimize()
    if status == OPTIMAL:
        return True
    elif status == INFEASIBLE:
        return False
    else:
        raise RuntimeError(
            "Could not determine stoichiometric consistencty."
            " Solver status is '{}'"
            " (only optimal or infeasible expected).".format(status))
Example #6
0
def check_stoichiometric_consistency(model):
    """
    Verify the consistency of the model stoichiometry.

    Parameters
    ----------
    model : cobra.Model
        The metabolic model under investigation.

    Notes
    -----
    See [1]_ section 3.1 for a complete description of the algorithm.

    .. [1] Gevorgyan, A., M. G Poolman, and D. A Fell.
           "Detection of Stoichiometric Inconsistencies in Biomolecular Models."
           Bioinformatics 24, no. 19 (2008): 2245.

    """
    Model, Constraint, Variable, Objective = con_helpers.get_interface(model)
    # The transpose of the stoichiometric matrix N.T in the paper.
    stoich_trans = Model()
    internal_rxns = con_helpers.get_internals(model)
    metabolites = set(met for rxn in internal_rxns for met in rxn.metabolites)
    LOGGER.info("model '%s' has %d internal reactions", model.id,
                len(internal_rxns))
    LOGGER.info("model '%s' has %d internal metabolites", model.id,
                len(metabolites))
    for metabolite in metabolites:
        stoich_trans.add(Variable(metabolite.id, lb=1))
    stoich_trans.update()
    con_helpers.add_reaction_constraints(stoich_trans, internal_rxns,
                                         Constraint)
    # The objective is to minimize the metabolite mass vector.
    stoich_trans.objective = Objective(1)
    stoich_trans.objective.set_linear_coefficients(
        {var: 1.
         for var in stoich_trans.variables})
    stoich_trans.objective.direction = "min"
    status = stoich_trans.optimize()
    if status == "optimal":
        return True
    elif status == "infeasible":
        return False
    else:
        raise RuntimeError(
            "Could not determine stoichiometric consistencty."
            " Solver status is '{}'"
            " (only optimal or infeasible expected).".format(status))
Example #7
0
def find_charge_imbalanced_reactions(model):
    """
    Find metabolic reactions that are not charge balanced.

    This will exclude biomass, exchange and demand reactions as they are
    unbalanced by definition. It will also fail all reactions where at
    least one metabolite does not have a charge defined.

    Parameters
    ----------
    model : cobra.Model
        The metabolic model under investigation.

    """
    internal_rxns = con_helpers.get_internals(model)
    return [
        rxn for rxn in internal_rxns if not con_helpers.is_charge_balanced(rxn)
    ]
Example #8
0
def test_find_mass_unbalanced_reactions(model, num):
    """Expect all reactions to be mass balanced."""
    internal_rxns = con_helpers.get_internals(model)
    reactions = consistency.find_mass_unbalanced_reactions(internal_rxns)
    assert len(reactions) == num
Example #9
0
def find_inconsistent_min_stoichiometry(model, atol=1e-13):
    """
    Detect inconsistent minimal net stoichiometries.

    Parameters
    ----------
    model : cobra.Model
        The metabolic model under investigation.
    atol : float, optional
        Values below the absolute tolerance are treated as zero. Expected to be
        very small but larger than zero.

    Notes
    -----
    See [1]_ section 3.3 for a complete description of the algorithm.

    References
    ----------
    .. [1] Gevorgyan, A., M. G Poolman, and D. A Fell.
           "Detection of Stoichiometric Inconsistencies in Biomolecular
           Models."
           Bioinformatics 24, no. 19 (2008): 2245.

    """
    if check_stoichiometric_consistency(model):
        return set()
    Model, Constraint, Variable, Objective = con_helpers.get_interface(model)
    unconserved_mets = find_unconserved_metabolites(model)
    LOGGER.info("model has %d unconserved metabolites", len(unconserved_mets))
    internal_rxns = con_helpers.get_internals(model)
    internal_mets = set(met for rxn in internal_rxns
                        for met in rxn.metabolites)
    get_id = attrgetter("id")
    reactions = sorted(internal_rxns, key=get_id)
    metabolites = sorted(internal_mets, key=get_id)
    stoich, met_index, rxn_index = con_helpers.stoichiometry_matrix(
        metabolites, reactions)
    left_ns = con_helpers.nullspace(stoich.T)
    # deal with numerical instabilities
    left_ns[np.abs(left_ns) < atol] = 0.0
    LOGGER.info("nullspace has dimension %d", left_ns.shape[1])
    inc_minimal = set()
    (problem,
     indicators) = con_helpers.create_milp_problem(left_ns, metabolites, Model,
                                                   Variable, Constraint,
                                                   Objective)
    LOGGER.debug(str(problem))
    cuts = list()
    for met in unconserved_mets:
        row = met_index[met]
        if (left_ns[row] == 0.0).all():
            LOGGER.debug("%s: singleton minimal unconservable set", met.id)
            # singleton set!
            inc_minimal.add((met, ))
            continue
        # expect a positive mass for the unconserved metabolite
        problem.variables[met.id].lb = 1e-3
        status = problem.optimize()
        while status == "optimal":
            LOGGER.debug("%s: status %s", met.id, status)
            LOGGER.debug("sum of all primal values: %f",
                         sum(problem.primal_values.values()))
            LOGGER.debug("sum of binary indicators: %f",
                         sum(var.primal for var in indicators))
            solution = [
                model.metabolites.get_by_id(var.name[2:]) for var in indicators
                if var.primal > 0.2
            ]
            LOGGER.debug("%s: set size %d", met.id, len(solution))
            inc_minimal.add(tuple(solution))
            if len(solution) == 1:
                break
            cuts.append(
                con_helpers.add_cut(problem, indicators,
                                    len(solution) - 1, Constraint))
            status = problem.optimize()
        LOGGER.debug("%s: last status %s", met.id, status)
        # reset
        problem.variables[met.id].lb = 0.0
        problem.remove(cuts)
        cuts.clear()
    return inc_minimal
Example #10
0
def test_find_mass_unbalanced_reactions(model, num):
    """Expect all reactions to be mass balanced."""
    internal_rxns = con_helpers.get_internals(model)
    reactions = consistency.find_mass_unbalanced_reactions(internal_rxns)
    assert len(reactions) == num