def test_dense_matrix(model: "Model") -> None: """Test dense stoichiometric matrix creation.""" S = create_stoichiometric_matrix(model, array_type="dense", dtype=int) assert S.dtype == int assert np.allclose(S.max(), [59]) S_df = create_stoichiometric_matrix(model, array_type="DataFrame", dtype=int) assert S_df.values.dtype == int assert np.all(S_df.columns == [r.id for r in model.reactions]) assert np.all(S_df.index == [m.id for m in model.metabolites]) assert np.allclose(S_df.values, S) S = create_stoichiometric_matrix(model, array_type="dense", dtype=float) solution = model.optimize() mass_balance = S.dot(solution.fluxes) assert np.allclose(mass_balance, 0)
def create_model_files(self, temp_dir): """ Write stochiometry data, reaction reversibilities, metabolite, and reaction names to temporary files in preparation for calling efmtool """ stoich_mat = create_stoichiometric_matrix(self.model) # Stoichiometric Matrix np.savetxt(temp_dir + '/stoich.txt', stoich_mat, delimiter='\t') # Reaction reversibilities np.savetxt(temp_dir + '/revs.txt', np.array([r.reversibility for r in self.model.reactions]), delimiter='\t', fmt='%d', newline='\t') # Reaction Names with open(temp_dir + '/rnames.txt', 'w') as f: f.write('\t'.join( ('"{}"'.format(r.id) for r in self.model.reactions))) # Metabolite Names with open(temp_dir + '/mnames.txt', 'w') as f: f.write('\t'.join( ('"{}"'.format(m.id) for m in self.model.metabolites)))
def test_dense_matrix(model): S = create_stoichiometric_matrix(model, array_type='dense', dtype=int) assert S.dtype == int assert np.allclose(S.max(), [59]) S_df = create_stoichiometric_matrix( model, array_type='DataFrame', dtype=int) assert S_df.values.dtype == int assert np.all(S_df.columns == [r.id for r in model.reactions]) assert np.all(S_df.index == [m.id for m in model.metabolites]) assert np.allclose(S_df.values, S) S = create_stoichiometric_matrix(model, array_type='dense', dtype=float) solution = model.optimize() mass_balance = S.dot(solution.fluxes) assert np.allclose(mass_balance, 0)
def test_sparse_matrix(self, model): sparse_types = ['dok', 'lil'] solution = model.optimize() for sparse_type in sparse_types: S = create_stoichiometric_matrix(model, array_type=sparse_type) mass_balance = S.dot(solution.fluxes) assert numpy.allclose(mass_balance, 0)
def test_sparse_matrix(model): sparse_types = ['dok', 'lil'] solution = model.optimize() for sparse_type in sparse_types: S = create_stoichiometric_matrix(model, array_type=sparse_type) mass_balance = S.dot(solution.fluxes) assert np.allclose(mass_balance, 0)
def test_dense_matrix(self, model): S = create_stoichiometric_matrix(model, array_type='dense', dtype=int) assert S.dtype == int assert numpy.allclose(S.max(), [59]) S_df = create_stoichiometric_matrix(model, array_type='DataFrame', dtype=int) assert S_df.values.dtype == int assert numpy.all(S_df.columns == [r.id for r in model.reactions]) assert numpy.all(S_df.index == [m.id for m in model.metabolites]) assert numpy.allclose(S_df.values, S) S = create_stoichiometric_matrix(model, array_type='dense', dtype=float) solution = model.optimize() mass_balance = S.dot(solution.fluxes) assert numpy.allclose(mass_balance, 0)
def test_sparse_matrix(model): """Test sparse stoichiometric matrix creation.""" pytest.importorskip("scipy") sparse_types = ["dok", "lil"] solution = model.optimize() for sparse_type in sparse_types: S = create_stoichiometric_matrix(model, array_type=sparse_type) mass_balance = S.dot(solution.fluxes) assert np.allclose(mass_balance, 0)
def __init__(self, model, thinning, seed=None): self.model = model self.thinning = thinning self.n_samples = 0 self.S = create_stoichiometric_matrix(model, array_type='dense') self.NS = nullspace(self.S) self.bounds = np.array([[r.lower_bound, r.upper_bound] for r in model.reactions]).T self.fixed = np.diff(self.bounds, axis=0).flatten() < 2 * BTOL self.warmup = None if seed is None: self._seed = int(time()) else: self._seed = seed # Avoid overflow self._seed = self._seed % np.iinfo(np.int32).max
def prob_vertex(model): #the probability of a steadystate random walk through hyperedges on hypergraph G(V,E) visiting vertex vi # Create stoichiometric matrix S = create_stoichiometric_matrix(model, array_type='DataFrame') #Create hypergraph incidence matrix H = np.zeros(S.shape) H[S != 0] = 1 #Vertex degree d = H.sum(axis=1) #Probability of a steadystate random walk visiting vertex v Pg = d / d.sum() return Pg
def makeHypergraph(model): S = create_stoichiometric_matrix(model, array_type='DataFrame') H = DirectedHypergraph() for i in range(len(S.columns)): nodes_df = S.iloc[:, i][S.iloc[:, i] != 0] edge_name = nodes_df.name head_nodes = set(nodes_df[nodes_df > 0].index) tail_nodes = set(nodes_df[nodes_df < 0].index) H.add_hyperedge(head_nodes, tail_nodes, {'reaction': edge_name}) if model.reactions.get_by_id(edge_name).reversibility: H.add_hyperedge(tail_nodes, head_nodes, {'reaction': edge_name + '_rev'}) return H
def create_mat_dict(model): """create a dict mapping model attributes to arrays""" rxns = model.reactions mets = model.metabolites mat = OrderedDict() mat["mets"] = _cell([met_id for met_id in create_mat_metabolite_id(model)]) mat["metNames"] = _cell(mets.list_attr("name")) mat["metFormulas"] = _cell([str(m.formula) for m in mets]) try: mat["metCharge"] = array(mets.list_attr("charge")) * 1. except TypeError: # can't have any None entries for charge, or this will fail pass mat["genes"] = _cell(model.genes.list_attr("id")) # make a matrix for rxnGeneMat # reactions are rows, genes are columns rxn_gene = scipy_sparse.dok_matrix((len(model.reactions), len(model.genes))) if min(rxn_gene.shape) > 0: for i, reaction in enumerate(model.reactions): for gene in reaction.genes: rxn_gene[i, model.genes.index(gene)] = 1 mat["rxnGeneMat"] = rxn_gene mat["grRules"] = _cell(rxns.list_attr("gene_reaction_rule")) mat["rxns"] = _cell(rxns.list_attr("id")) mat["rxnNames"] = _cell(rxns.list_attr("name")) mat["subSystems"] = _cell(rxns.list_attr("subsystem")) mat["csense"] = "".join(( met._constraint_sense for met in model.metabolites)) stoich_mat = create_stoichiometric_matrix(model) mat["S"] = stoich_mat if stoich_mat is not None else [[]] # multiply by 1 to convert to float, working around scipy bug # https://github.com/scipy/scipy/issues/4537 mat["lb"] = array(rxns.list_attr("lower_bound")) * 1. mat["ub"] = array(rxns.list_attr("upper_bound")) * 1. mat["b"] = array(mets.list_attr("_bound")) * 1. mat["c"] = array(rxns.list_attr("objective_coefficient")) * 1. mat["rev"] = array(rxns.list_attr("reversibility")) * 1 mat["description"] = str(model.id) return mat
def create_mat_dict(model): """create a dict mapping model attributes to arrays""" rxns = model.reactions mets = model.metabolites mat = OrderedDict() mat["mets"] = _cell([met_id for met_id in create_mat_metabolite_id(model)]) mat["metNames"] = _cell(mets.list_attr("name")) mat["metFormulas"] = _cell([str(m.formula) for m in mets]) try: mat["metCharge"] = array(mets.list_attr("charge")) * 1. except TypeError: # can't have any None entries for charge, or this will fail pass mat["genes"] = _cell(model.genes.list_attr("id")) # make a matrix for rxnGeneMat # reactions are rows, genes are columns rxn_gene = scipy_sparse.dok_matrix( (len(model.reactions), len(model.genes))) if min(rxn_gene.shape) > 0: for i, reaction in enumerate(model.reactions): for gene in reaction.genes: rxn_gene[i, model.genes.index(gene)] = 1 mat["rxnGeneMat"] = rxn_gene mat["grRules"] = _cell(rxns.list_attr("gene_reaction_rule")) mat["rxns"] = _cell(rxns.list_attr("id")) mat["rxnNames"] = _cell(rxns.list_attr("name")) mat["subSystems"] = _cell(rxns.list_attr("subsystem")) mat["csense"] = "".join( (met._constraint_sense for met in model.metabolites)) stoich_mat = create_stoichiometric_matrix(model) mat["S"] = stoich_mat if stoich_mat is not None else [[]] # multiply by 1 to convert to float, working around scipy bug # https://github.com/scipy/scipy/issues/4537 mat["lb"] = array(rxns.list_attr("lower_bound")) * 1. mat["ub"] = array(rxns.list_attr("upper_bound")) * 1. mat["b"] = array(mets.list_attr("_bound")) * 1. mat["c"] = array(rxns.list_attr("objective_coefficient")) * 1. mat["rev"] = array(rxns.list_attr("reversibility")) * 1 mat["description"] = str(model.id) return mat
def test_with_core_model(self, core_model): s = create_stoichiometric_matrix(core_model) ns = nullspace(s) assert round(abs(np.abs(s.dot(ns)).max() - 0), 10) == 0
import hashlib _ = 123456789 # just a wrong number, please ignore ###### Stop ignoring # Some stuff you can/should use ... # feel free to import additional things from those packages already imported # or the Python Standard Library (https://docs.python.org/3/library/) # (if it helps) but do not import other 3rd party packages. import numpy as np from cobra.io import read_sbml_model from cobra.util import create_stoichiometric_matrix # Read model (central metabolism model of Escherichia coli) model = read_sbml_model('e_coli_core.xml') # Create stoichiometric matrix S = create_stoichiometric_matrix(model) # 1. Binarize S # replace _ with a binary representation of S. # S_bin should only contain floating point numbers (either 0. or 1.) S_bin = _ ###### Don't touch def test_binary_stoichiometry_matrix(): assert hashlib.md5(S_bin).digest( ) == b'\xcd\xd3\x04N\x9e\xaf\x1f\xc5\xcb9\x9f\xaaa\x00\x06['
def validate(self, samples): """Validate a set of samples for equality and inequality feasibility. Can be used to check whether the generated samples and warmup points are feasible. Parameters ---------- samples : numpy.matrix Must be of dimension (n_samples x n_reactions). Contains the samples to be validated. Samples must be from fluxes. Returns ------- numpy.array A one-dimensional numpy array of length containing a code of 1 to 3 letters denoting the validation result: - 'v' means feasible in bounds and equality constraints - 'l' means a lower bound violation - 'u' means a lower bound validation - 'e' means and equality constraint violation """ samples = np.atleast_2d(samples) prob = self.problem if samples.shape[1] == len(self.model.reactions): S = create_stoichiometric_matrix(self.model) b = np.array([ self.model.constraints[m.id].lb for m in self.model.metabolites ]) bounds = np.array([r.bounds for r in self.model.reactions]).T elif samples.shape[1] == len(self.model.variables): S = prob.equalities b = prob.b bounds = prob.variable_bounds else: raise ValueError("Wrong number of columns. samples must have a " "column for each flux or variable defined in the " "model!") feasibility = np.abs(S.dot(samples.T).T - b) feasibility = feasibility.max(axis=1) lb_error = (samples - bounds[0, ]).min(axis=1) ub_error = (bounds[1, ] - samples).min(axis=1) if (samples.shape[1] == len(self.model.variables) and prob.inequalities.shape[0]): consts = prob.inequalities.dot(samples.T) lb_error = np.minimum(lb_error, (consts - prob.bounds[0, ]).min(axis=1)) ub_error = np.minimum(ub_error, (prob.bounds[1, ] - consts).min(axis=1)) valid = ((feasibility < feasibility_tol) & (lb_error > -bounds_tol) & (ub_error > -bounds_tol)) codes = np.repeat("", valid.shape[0]).astype(np.dtype((str, 3))) codes[valid] = "v" codes[lb_error <= -bounds_tol] = np.char.add( codes[lb_error <= -bounds_tol], "l") codes[ub_error <= -bounds_tol] = np.char.add( codes[ub_error <= -bounds_tol], "u") codes[feasibility > feasibility_tol] = np.char.add( codes[feasibility > feasibility_tol], "e") return codes
def validate(self, samples): """Validate a set of samples for equality and inequality feasibility. Can be used to check whether the generated samples and warmup points are feasible. Parameters ---------- samples : numpy.matrix Must be of dimension (n_samples x n_reactions). Contains the samples to be validated. Samples must be from fluxes. Returns ------- numpy.array A one-dimensional numpy array of length containing a code of 1 to 3 letters denoting the validation result: - 'v' means feasible in bounds and equality constraints - 'l' means a lower bound violation - 'u' means a lower bound validation - 'e' means and equality constraint violation """ samples = np.atleast_2d(samples) prob = self.problem if samples.shape[1] == len(self.model.reactions): S = create_stoichiometric_matrix(self.model) b = np.array([self.model.constraints[m.id].lb for m in self.model.metabolites]) bounds = np.array([r.bounds for r in self.model.reactions]).T elif samples.shape[1] == len(self.model.variables): S = prob.equalities b = prob.b bounds = prob.variable_bounds else: raise ValueError("Wrong number of columns. samples must have a " "column for each flux or variable defined in the " "model!") feasibility = np.abs(S.dot(samples.T).T - b).max(axis=1) lb_error = (samples - bounds[0, ]).min(axis=1) ub_error = (bounds[1, ] - samples).min(axis=1) if (samples.shape[1] == len(self.model.variables) and prob.inequalities.shape[0]): consts = prob.inequalities.dot(samples.T) lb_error = np.minimum( lb_error, (consts - prob.bounds[0, ]).min(axis=1)) ub_error = np.minimum( ub_error, (prob.bounds[1, ] - consts).min(axis=1) ) valid = ( (feasibility < self.feasibility_tol) & (lb_error > -self.bounds_tol) & (ub_error > -self.bounds_tol)) codes = np.repeat("", valid.shape[0]).astype(np.dtype((str, 3))) codes[valid] = "v" codes[lb_error <= -self.bounds_tol] = np.char.add( codes[lb_error <= -self.bounds_tol], "l") codes[ub_error <= -self.bounds_tol] = np.char.add( codes[ub_error <= -self.bounds_tol], "u") codes[feasibility > self.feasibility_tol] = np.char.add( codes[feasibility > self.feasibility_tol], "e") return codes
def add_loopless(model, zero_cutoff=None): """Modify a model so all feasible flux distributions are loopless. In most cases you probably want to use the much faster `loopless_solution`. May be used in cases where you want to add complex constraints and objecives (for instance quadratic objectives) to the model afterwards or use an approximation of Gibbs free energy directions in you model. Adds variables and constraints to a model which will disallow flux distributions with loops. The used formulation is described in [1]_. This function *will* modify your model. Parameters ---------- model : cobra.Model The model to which to add the constraints. zero_cutoff : positive float, optional Cutoff used for null space. Coefficients with an absolute value smaller than `zero_cutoff` are considered to be zero (default model.tolerance). Returns ------- Nothing References ---------- .. [1] Elimination of thermodynamically infeasible loops in steady-state metabolic models. Schellenberger J, Lewis NE, Palsson BO. Biophys J. 2011 Feb 2;100(3):544-53. doi: 10.1016/j.bpj.2010.12.3707. Erratum in: Biophys J. 2011 Mar 2;100(5):1381. """ zero_cutoff = normalize_cutoff(model, zero_cutoff) internal = [i for i, r in enumerate(model.reactions) if not r.boundary] s_int = create_stoichiometric_matrix(model)[:, numpy.array(internal)] n_int = nullspace(s_int).T max_bound = max(max(abs(b) for b in r.bounds) for r in model.reactions) prob = model.problem # Add indicator variables and new constraints to_add = [] for i in internal: rxn = model.reactions[i] # indicator variable a_i indicator = prob.Variable("indicator_" + rxn.id, type="binary") # -M*(1 - a_i) <= v_i <= M*a_i on_off_constraint = prob.Constraint( rxn.flux_expression - max_bound * indicator, lb=-max_bound, ub=0, name="on_off_" + rxn.id) # -(max_bound + 1) * a_i + 1 <= G_i <= -(max_bound + 1) * a_i + 1000 delta_g = prob.Variable("delta_g_" + rxn.id) delta_g_range = prob.Constraint( delta_g + (max_bound + 1) * indicator, lb=1, ub=max_bound, name="delta_g_range_" + rxn.id) to_add.extend([indicator, on_off_constraint, delta_g, delta_g_range]) model.add_cons_vars(to_add) # Add nullspace constraints for G_i for i, row in enumerate(n_int): name = "nullspace_constraint_" + str(i) nullspace_constraint = prob.Constraint(Zero, lb=0, ub=0, name=name) model.add_cons_vars([nullspace_constraint]) coefs = {model.variables[ "delta_g_" + model.reactions[ridx].id]: row[i] for i, ridx in enumerate(internal) if abs(row[i]) > zero_cutoff} model.constraints[name].set_linear_coefficients(coefs)
def add_loopless(model, zero_cutoff=1e-12): """Modify a model so all feasible flux distributions are loopless. In most cases you probably want to use the much faster `loopless_solution`. May be used in cases where you want to add complex constraints and objecives (for instance quadratic objectives) to the model afterwards or use an approximation of Gibbs free energy directions in you model. Adds variables and constraints to a model which will disallow flux distributions with loops. The used formulation is described in [1]_. This function *will* modify your model. Parameters ---------- model : cobra.Model The model to which to add the constraints. zero_cutoff : positive float, optional Cutoff used for null space. Coefficients with an absolute value smaller than `zero_cutoff` are considered to be zero. Returns ------- Nothing References ---------- .. [1] Elimination of thermodynamically infeasible loops in steady-state metabolic models. Schellenberger J, Lewis NE, Palsson BO. Biophys J. 2011 Feb 2;100(3):544-53. doi: 10.1016/j.bpj.2010.12.3707. Erratum in: Biophys J. 2011 Mar 2;100(5):1381. """ internal = [i for i, r in enumerate(model.reactions) if not r.boundary] s_int = create_stoichiometric_matrix(model)[:, numpy.array(internal)] n_int = nullspace(s_int).T max_bound = max(max(abs(b) for b in r.bounds) for r in model.reactions) prob = model.problem # Add indicator variables and new constraints to_add = [] for i in internal: rxn = model.reactions[i] # indicator variable a_i indicator = prob.Variable("indicator_" + rxn.id, type="binary") # -M*(1 - a_i) <= v_i <= M*a_i on_off_constraint = prob.Constraint( rxn.flux_expression - max_bound * indicator, lb=-max_bound, ub=0, name="on_off_" + rxn.id) # -(max_bound + 1) * a_i + 1 <= G_i <= -(max_bound + 1) * a_i + 1000 delta_g = prob.Variable("delta_g_" + rxn.id) delta_g_range = prob.Constraint( delta_g + (max_bound + 1) * indicator, lb=1, ub=max_bound, name="delta_g_range_" + rxn.id) to_add.extend([indicator, on_off_constraint, delta_g, delta_g_range]) model.add_cons_vars(to_add) # Add nullspace constraints for G_i for i, row in enumerate(n_int): name = "nullspace_constraint_" + str(i) nullspace_constraint = prob.Constraint(Zero, lb=0, ub=0, name=name) model.add_cons_vars([nullspace_constraint]) coefs = {model.variables[ "delta_g_" + model.reactions[ridx].id]: row[i] for i, ridx in enumerate(internal) if abs(row[i]) > zero_cutoff} model.constraints[name].set_linear_coefficients(coefs)
def test_with_core_model(self, core_model): s = create_stoichiometric_matrix(core_model) ns = nullspace(s) assert round(abs(np.abs(s.dot(ns)).max() - 0), 10) == 0