Source code for memote.support.consistency_helpers

# -*- coding: utf-8 -*-

# Copyright 2017 Novo Nordisk Foundation Center for Biosustainability,
# Technical University of Denmark.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Helper functions for stoichiometric consistency checks."""

from __future__ import absolute_import

import logging

import numpy as np
import sympy
from numpy.linalg import svd
from six import iteritems
from builtins import zip, dict

__all__ = (
    "stoichiometry_matrix",
    "nullspace"
)

LOGGER = logging.getLogger(__name__)


def add_reaction_constraints(model, reactions, Constraint):
    """
    Add the stoichiometric coefficients as constraints.

    Parameters
    ----------
    model : optlang.Model
        The transposed stoichiometric matrix representation.
    reactions : iterable
        Container of `cobra.Reaction` instances.
    Constraint : optlang.Constraint
        The constraint class for the specific interface.

    """
    for rxn in reactions:
        expression = sympy.Add(
            *[coefficient * model.variables[metabolite.id]
              for (metabolite, coefficient) in rxn.metabolites.items()])
        constraint = Constraint(expression, lb=0, ub=0, name=rxn.id)
        model.add(constraint)


[docs]def stoichiometry_matrix(metabolites, reactions): """ Return the stoichiometry matrix representation of a set of reactions. The reactions and metabolites order is respected. All metabolites are expected to be contained and complete in terms of the reactions. Parameters ---------- reactions : iterable A somehow ordered list of unique reactions. metabolites : iterable A somehow ordered list of unique metabolites. Returns ------- numpy.array The 2D array that represents the stoichiometry matrix. dict A dictionary mapping metabolites to row indexes. dict A dictionary mapping reactions to column indexes. """ matrix = np.zeros((len(metabolites), len(reactions))) met_index = dict((met, i) for i, met in enumerate(metabolites)) rxn_index = dict() for i, rxn in enumerate(reactions): rxn_index[rxn] = i for met, coef in iteritems(rxn.metabolites): j = met_index[met] matrix[j, i] = coef return matrix, met_index, rxn_index
[docs]def nullspace(matrix, atol=1e-13, rtol=0.0): """ Compute the nullspace of a 2D `numpy.array`. Notes ----- Adapted from: https://scipy.github.io/old-wiki/pages/Cookbook/RankNullspace.html """ matrix = np.atleast_2d(matrix) _, s, vh = svd(matrix) tol = max(atol, rtol * s[0]) return np.compress(s < tol, vh, axis=0).T
def get_interface(model): """ Return the interface specific classes. Parameters ---------- model : cobra.Model The metabolic model under investigation. """ return ( model.solver.interface.Model, model.solver.interface.Constraint, model.solver.interface.Variable, model.solver.interface.Objective ) def get_internals(model): """ Return non-exchange reactions and their metabolites. Exchange reactions are unbalanced by their nature. They are excluded here and only the metabolites of the others are considered. Parameters ---------- model : cobra.Model The metabolic model under investigation. """ internal_rxns = set(model.reactions) - set(model.exchanges) metabolites = set(met for rxn in internal_rxns for met in rxn.metabolites) LOGGER.info("model '%s' has %d internal metabolites", model.id, len(metabolites)) LOGGER.info("model '%s' has %d internal reactions", model.id, len(internal_rxns)) return internal_rxns, metabolites def create_milp_problem(kernel, metabolites, Model, Variable, Constraint, Objective): """ Create the MILP as defined by equation (13) in [1]_. Parameters ---------- kernel : numpy.array A 2-dimensional array that represents the left nullspace of the stoichiometric matrix which is the nullspace of the transpose of the stoichiometric matrix. metabolites : iterable The metabolites in the nullspace. The length of this vector must equal the first dimension of the nullspace. Model : optlang.Model Model class for a specific optlang interface. Variable : optlang.Variable Variable class for a specific optlang interface. Constraint : optlang.Constraint Constraint class for a specific optlang interface. Objective : optlang.Objective Objective class for a specific optlang interface. References ---------- .. [1] Gevorgyan, A., M. G Poolman, and D. A Fell. "Detection of Stoichiometric Inconsistencies in Biomolecular Models." Bioinformatics 24, no. 19 (2008): 2245. """ assert len(metabolites) == kernel.shape[0],\ "metabolite vector and first nullspace dimension must be equal" ns_problem = Model() k_vars = list() for met in metabolites: # The element y[i] of the mass vector. y_var = Variable(met.id) k_var = Variable("k_{}".format(met.id), type="binary") k_vars.append(k_var) ns_problem.add([y_var, k_var]) # This constraint is equivalent to 0 <= y[i] <= k[i]. ns_problem.add(Constraint( y_var - k_var, ub=0, name="switch_{}".format(met.id))) ns_problem.update() # add nullspace constraints for (j, column) in enumerate(kernel.T): expression = sympy.Add( *[coef * ns_problem.variables[met.id] for (met, coef) in zip(metabolites, column) if coef != 0.0]) constraint = Constraint(expression, lb=0, ub=0, name="ns_{}".format(j)) ns_problem.add(constraint) # The objective is to minimize the binary indicators k[i], subject to # the above inequality constraints. ns_problem.objective = Objective(1) ns_problem.objective.set_linear_coefficients( {k_var: 1. for k_var in k_vars}) ns_problem.objective.direction = "min" return ns_problem, k_vars def add_cut(problem, indicators, bound, Constraint): """ Add an integer cut to the problem. Ensure that the same solution involving these indicator variables cannot be found by enforcing their sum to be less than before. Parameters ---------- problem : optlang.Model Specific optlang interface Model instance. indicators : iterable Binary indicator `optlang.Variable`s. bound : int Should be one less than the sum of indicators. Corresponds to P - 1 in equation (14) in [1]_. Constraint : optlang.Constraint Constraint class for a specific optlang interface. References ---------- .. [1] Gevorgyan, A., M. G Poolman, and D. A Fell. "Detection of Stoichiometric Inconsistencies in Biomolecular Models." Bioinformatics 24, no. 19 (2008): 2245. """ cut = Constraint(sympy.Add(*indicators), ub=bound) problem.add(cut) return cut