def __build_problem(self): """Build the matrix representation of the sampling problem.""" # Set up the mathematical problem prob = constraint_matrices(self.model, zero_tol=feasibility_tol) # check if there any non-zero equality constraints equalities = prob.equalities b = prob.b homogeneous = all(np.abs(b) < feasibility_tol) fixed_non_zero = np.abs(prob.variable_bounds[:, 1]) > feasibility_tol fixed_non_zero &= prob.variable_fixed # check if there are any non-zero fixed variables, add them as # equalities to the stoichiometric matrix if any(fixed_non_zero): n_fixed = fixed_non_zero.sum() rows = np.zeros((n_fixed, prob.equalities.shape[1])) rows[range(n_fixed), np.where(fixed_non_zero)] = 1.0 equalities = np.vstack([equalities, rows]) var_b = prob.variable_bounds[:, 1] b = np.hstack([b, var_b[fixed_non_zero]]) homogeneous = False # Set up a projection that can cast point into the nullspace nulls = nullspace(prob.equalities) projection = nulls.dot(nulls.T) # convert bounds to a matrix and add variable bounds as well return Problem(equalities=equalities, b=b, inequalities=prob.inequalities, bounds=np.atleast_2d(prob.bounds).T, variable_fixed=prob.variable_fixed, variable_bounds=np.atleast_2d(prob.variable_bounds).T, projection=projection, homogeneous=homogeneous)
def __build_problem(self) -> Problem: """Build the matrix representation of the sampling problem. Returns ------- Problem The matrix representation in the form of a NamedTuple. """ # Set up the mathematical problem prob = constraint_matrices(self.model, zero_tol=self.feasibility_tol) # check if there any non-zero equality constraints equalities = prob.equalities b = prob.b bounds = np.atleast_2d(prob.bounds).T var_bounds = np.atleast_2d(prob.variable_bounds).T homogeneous = all(np.abs(b) < self.feasibility_tol) fixed_non_zero = np.abs(prob.variable_bounds[:, 1]) > self.feasibility_tol fixed_non_zero &= prob.variable_fixed # check if there are any non-zero fixed variables, add them as # equalities to the stoichiometric matrix if any(fixed_non_zero): n_fixed = fixed_non_zero.sum() rows = np.zeros((n_fixed, prob.equalities.shape[1])) rows[range(n_fixed), np.where(fixed_non_zero)] = 1.0 equalities = np.vstack([equalities, rows]) var_b = prob.variable_bounds[:, 1] b = np.hstack([b, var_b[fixed_non_zero]]) homogeneous = False # Set up a projection that can cast point into the nullspace nulls = nullspace(equalities) # convert bounds to a matrix and add variable bounds as well return Problem( equalities=shared_np_array(equalities.shape, equalities), b=shared_np_array(b.shape, b), inequalities=shared_np_array(prob.inequalities.shape, prob.inequalities), bounds=shared_np_array(bounds.shape, bounds), variable_fixed=shared_np_array(prob.variable_fixed.shape, prob.variable_fixed, integer=True), variable_bounds=shared_np_array(var_bounds.shape, var_bounds), nullspace=shared_np_array(nulls.shape, nulls), homogeneous=homogeneous, )
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 __build_problem(self): """Build the matrix representation of the sampling problem.""" # Set up the mathematical problem prob = constraint_matrices(self.model, zero_tol=self.feasibility_tol) # check if there any non-zero equality constraints equalities = prob.equalities b = prob.b bounds = np.atleast_2d(prob.bounds).T var_bounds = np.atleast_2d(prob.variable_bounds).T homogeneous = all(np.abs(b) < self.feasibility_tol) fixed_non_zero = np.abs(prob.variable_bounds[:, 1]) > \ self.feasibility_tol fixed_non_zero &= prob.variable_fixed # check if there are any non-zero fixed variables, add them as # equalities to the stoichiometric matrix if any(fixed_non_zero): n_fixed = fixed_non_zero.sum() rows = np.zeros((n_fixed, prob.equalities.shape[1])) rows[range(n_fixed), np.where(fixed_non_zero)] = 1.0 equalities = np.vstack([equalities, rows]) var_b = prob.variable_bounds[:, 1] b = np.hstack([b, var_b[fixed_non_zero]]) homogeneous = False # Set up a projection that can cast point into the nullspace nulls = nullspace(equalities) # convert bounds to a matrix and add variable bounds as well return Problem( equalities=shared_np_array(equalities.shape, equalities), b=shared_np_array(b.shape, b), inequalities=shared_np_array(prob.inequalities.shape, prob.inequalities), bounds=shared_np_array(bounds.shape, bounds), variable_fixed=shared_np_array(prob.variable_fixed.shape, prob.variable_fixed, integer=True), variable_bounds=shared_np_array(var_bounds.shape, var_bounds), nullspace=shared_np_array(nulls.shape, nulls), homogeneous=homogeneous )
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_array(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(S.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)