def _get_expression(self): if self.problem is not None and self._expression_expired and len( self.problem._variables) > 0: grb_obj = self.problem.problem.getObjective() variables = self.problem._variables if self.problem.problem.IsQP: quadratic_expression = symbolics.add([ symbolics.Real(grb_obj.getCoeff(i)) * variables[grb_obj.getVar1(i).VarName] * variables[grb_obj.getVar2(i).VarName] for i in range(grb_obj.size()) ]) linear_objective = grb_obj.getLinExpr() else: quadratic_expression = symbolics.Real(0.0) linear_objective = grb_obj linear_expression = symbolics.add([ symbolics.Real(linear_objective.getCoeff(i)) * variables[linear_objective.getVar(i).VarName] for i in range(linear_objective.size()) ]) self._expression = (linear_expression + quadratic_expression + getattr(self.problem, "_objective_offset", 0)) self._expression_expired = False return self._expression
def __init__(self, problem=None, *args, **kwargs): super(Model, self).__init__(*args, **kwargs) if problem is None: self.problem = gurobipy.Model() self.problem.params.OutputFlag = 0 if self.name is not None: self.problem.setAttr('ModelName', self.name) self.problem.update() elif isinstance(problem, gurobipy.Model): self.problem = problem variables = [] for gurobi_variable in self.problem.getVars(): variables.append(Variable( gurobi_variable.getAttr("VarName"), lb=gurobi_variable.getAttr("lb"), ub=gurobi_variable.getAttr("ub"), problem=self, type=_GUROBI_VTYPE_TO_VTYPE[gurobi_variable.getAttr("vType")] )) super(Model, self)._add_variables(variables) constraints = [] for gurobi_constraint in self.problem.getConstrs(): sense = gurobi_constraint.Sense name = gurobi_constraint.getAttr("ConstrName") rhs = gurobi_constraint.RHS row = self.problem.getRow(gurobi_constraint) lhs = symbolics.add( [symbolics.Real(row.getCoeff(i)) * self.variables[row.getVar(i).VarName] for i in range(row.size())] ) if sense == '=': constraint = Constraint(lhs, name=name, lb=rhs, ub=rhs, problem=self) elif sense == '>': constraint = Constraint(lhs, name=name, lb=rhs, ub=None, problem=self) elif sense == '<': constraint = Constraint(lhs, name=name, lb=None, ub=rhs, problem=self) else: raise ValueError('{} is not a valid sense'.format(sense)) constraints.append(constraint) super(Model, self)._add_constraints(constraints, sloppy=True) gurobi_objective = self.problem.getObjective() linear_expression = symbolics.add( [symbolics.Real(gurobi_objective.getCoeff(i)) * self.variables[gurobi_objective.getVar(i).VarName] for i in range(gurobi_objective.size())] ) self._objective = Objective( linear_expression, problem=self, direction={1: 'min', -1: 'max'}[self.problem.getAttr('ModelSense')] ) else: raise TypeError("Provided problem is not a valid Gurobi model.") self.configuration = Configuration(problem=self, verbosity=0)
def _get_expression(self): if self.problem is not None: coefficients_dict = self.problem.problem.objective coefficients_dict = { self.problem._variables[name]: coef for name, coef in coefficients_dict.items() if name in self.problem._variables } self._expression = symbolics.add(*(v * k for k, v in coefficients_dict.items())) + self.problem.problem.offset return self._expression
def _get_expression(self): if (self.problem is not None and self._expression_expired and len(self.problem._variables) > 0): model = self.problem vars = model._variables expression = add([ coef * vars[vn] for vn, coef in six.iteritems(model.problem.obj_linear_coefs) ]) q_ex = add([ coef * vars[vn[0]] * vars[vn[1]] for vn, coef in six.iteritems( model.problem.obj_quadratic_coefs) ]) expression += q_ex self._expression = expression self._expression_expired = False return self._expression
def _get_expression(self): if self.problem is not None: variables = self.problem._variables all_coefs = self.problem.problem.constraint_coefs coefs = [(v, all_coefs.get((self.name, v.name), 0.0)) for v in variables] expression = add( [mul((symbolics.Real(co), v)) for (v, co) in coefs]) self._expression = expression return self._expression
def test_construct_with_sloppy(self): x, y, z, w = self.model.variables[:4] obj = self.interface.Objective( symbolics.add([symbolics.mul((symbolics.One, var)) for var in [x, y, z]]), direction="min", sloppy=True ) self.model.objective = obj self.assertTrue(obj.get_linear_coefficients([x, y, z, w]) == {x: 1, y: 1, z: 1, w: 0})
def _get_expression(self): if self.problem is not None and self._expression_expired and len(self.problem._variables) > 0: cplex_problem = self.problem.problem coeffs = cplex_problem.objective.get_linear() expression = add([coeff * var for coeff, var in zip(coeffs, self.problem._variables) if coeff != 0.]) if cplex_problem.objective.get_num_quadratic_nonzeros() > 0: expression += self.problem._get_quadratic_expression(cplex_problem.objective.get_quadratic()) self._expression = expression + getattr(self.problem, "_objective_offset", 0) self._expression_expired = False return self._expression
def _get_expression(self): if self.problem is not None and self._expression_expired and len(self.problem._variables) > 0: cplex_problem = self.problem.problem coeffs = cplex_problem.objective.get_linear() expression = add([coeff * var for coeff, var in zip(coeffs, self.problem._variables) if coeff != 0.]) if cplex_problem.objective.get_num_quadratic_nonzeros() > 0: expression += self.problem._get_quadratic_expression(cplex_problem.objective.get_quadratic()) self._expression = expression + getattr(self.problem, "_objective_offset", 0) self._expression_expired = False return self._expression
def test_construct_with_sloppy(self): x, y, z, w = self.model.variables[:4] obj = self.interface.Objective( symbolics.add([symbolics.mul((symbolics.One, var)) for var in [x, y, z]]), direction="min", sloppy=True ) self.model.objective = obj self.assertTrue(obj.get_linear_coefficients([x, y, z, w]) == {x: 1, y: 1, z: 1, w: 0})
def test_construct_with_sloppy(self): x, y, z, w = self.model.variables[:4] const = self.interface.Constraint( symbolics.add([symbolics.mul(symbolics.One, var) for var in [x, y, z]]), lb=0, sloppy=True ) self.model.add(const) self.model.update() self.assertTrue(const.get_linear_coefficients([x, y, z, w]) == {x: 1, y: 1, z: 1, w: 0})
def test_construct_with_sloppy(self): x, y, z, w = self.model.variables[:4] const = self.interface.Constraint( symbolics.add([symbolics.mul(symbolics.One, var) for var in [x, y, z]]), lb=0, sloppy=True ) self.model.add(const) self.model.update() self.assertTrue(const.get_linear_coefficients([x, y, z, w]) == {x: 1, y: 1, z: 1, w: 0})
def _get_expression(self): if self.problem is not None: cplex_problem = self.problem.problem cplex_row = cplex_problem.linear_constraints.get_rows(self.name) variables = self.problem._variables expression = add([ mul((symbolics.Real(cplex_row.val[i]), variables[ind])) for i, ind in enumerate(cplex_row.ind) ]) self._expression = expression return self._expression
def set_linear_coefficients(self, coefficients): if self.problem is not None: self.problem.update() lb, ub = self.lb, self.ub self.lb, self.ub = None, None coefficients_dict = self.coefficient_dict(names=False) coefficients_dict.update(coefficients) self._expression = symbolics.add(*(v * k for k, v in coefficients_dict.items())) self.lb = lb self.ub = ub else: raise Exception("Can't change coefficients if constraint is not associated with a model.")
def _get_expression(self): if self.problem is not None: coefficients_dict = self.problem.problem.objective coefficients_dict = { self.problem._variables[name]: coef for name, coef in coefficients_dict.items() if name in self.problem._variables } self._expression = symbolics.add( *(v * k for k, v in coefficients_dict.items() )) + self.problem.problem.offset return self._expression
def _get_expression(self): if self.problem is not None and self._expression_expired and len(self.problem._variables) > 0: grb_obj = self.problem.problem.getObjective() terms = [] variables = self.problem._variables for i in range(grb_obj.size()): terms.append(grb_obj.getCoeff(i) * variables[grb_obj.getVar(i).getAttr('VarName')]) expression = symbolics.add(terms) # TODO implement quadratic objectives self._expression = expression + getattr(self.problem, "_objective_offset", 0) self._expression_expired = False return self._expression
def _get_expression(self): if self.problem is not None: col_num = glp_get_num_cols(self.problem.problem) ia = intArray(col_num + 1) da = doubleArray(col_num + 1) nnz = glp_get_mat_row(self.problem.problem, self._index, ia, da) constraint_variables = [self.problem._variables[glp_get_col_name(self.problem.problem, ia[i])] for i in range(1, nnz + 1)] expression = symbolics.add( [symbolics.mul((symbolics.Real(da[i]), constraint_variables[i - 1])) for i in range(1, nnz + 1)]) self._expression = expression return self._expression
def _get_expression(self): if self.problem is not None: col_num = glp_get_num_cols(self.problem.problem) ia = intArray(col_num + 1) da = doubleArray(col_num + 1) nnz = glp_get_mat_row(self.problem.problem, self._index, ia, da) constraint_variables = [self.problem._variables[glp_get_col_name(self.problem.problem, ia[i])] for i in range(1, nnz + 1)] expression = symbolics.add( [symbolics.mul((symbolics.Real(da[i]), constraint_variables[i - 1])) for i in range(1, nnz + 1)]) self._expression = expression return self._expression
def _get_expression(self): if self.problem is not None and self._expression_expired: variables = self.problem._variables def term_generator(): for index in range(1, glp_get_num_cols(self.problem.problem) + 1): coeff = glp_get_obj_coef(self.problem.problem, index) if coeff != 0.: yield (symbolics.Real(coeff), variables[index - 1]) expression = symbolics.add([symbolics.mul(term) for term in term_generator()]) self._expression = expression + getattr(self.problem, "_objective_offset", 0) self._expression_expired = False return self._expression
def _get_expression(self): if self.problem is not None and self._expression_expired and len(self.problem._variables) > 0: grb_obj = self.problem.problem.getObjective() variables = self.problem._variables if self.problem.problem.IsQP: quadratic_expression = symbolics.add( [symbolics.Real(grb_obj.getCoeff(i)) * variables[grb_obj.getVar1(i).VarName] * variables[grb_obj.getVar2(i).VarName] for i in range(grb_obj.size())]) linear_objective = grb_obj.getLinExpr() else: quadratic_expression = symbolics.Real(0.0) linear_objective = grb_obj linear_expression = symbolics.add( [symbolics.Real(linear_objective.getCoeff(i)) * variables[linear_objective.getVar(i).VarName] for i in range(linear_objective.size())] ) self._expression = (linear_expression + quadratic_expression + getattr(self.problem, "_objective_offset", 0)) self._expression_expired = False return self._expression
def _get_expression(self): if self.problem is not None and self._expression_expired: variables = self.problem._variables def term_generator(): for index in range(1, glp_get_num_cols(self.problem.problem) + 1): coeff = glp_get_obj_coef(self.problem.problem, index) if coeff != 0.: yield (symbolics.Real(coeff), variables[index - 1]) expression = symbolics.add([symbolics.mul(term) for term in term_generator()]) self._expression = expression + getattr(self.problem, "_objective_offset", 0) self._expression_expired = False return self._expression
def set_linear_coefficients(self, coefficients): if self.problem is not None: lb, ub = self.lb, self.ub self.lb, self.ub = None, None coefficients_dict = self.coefficient_dict(names=False) coefficients_dict.update(coefficients) self._expression = symbolics.add( *(v * k for k, v in coefficients_dict.items())) self.lb = lb self.ub = ub else: raise Exception( "Can't change coefficients if constraint is not associated with a model." )
def _get_expression(self): if self.problem is not None: gurobi_problem = self.problem.problem gurobi_constraint = self._internal_constraint row = gurobi_problem.getRow(gurobi_constraint) terms = [] for i in range(row.size()): internal_var_name = row.getVar(i).VarName if internal_var_name == self.name + '_aux': continue variable = self.problem._variables[internal_var_name] coeff = symbolics.Real(row.getCoeff(i)) terms.append(symbolics.mul((coeff, variable))) self._expression = symbolics.add(terms) return self._expression
def _get_expression(self): if self.problem is not None: gurobi_problem = self.problem.problem gurobi_constraint = self._internal_constraint row = gurobi_problem.getRow(gurobi_constraint) terms = [] for i in range(row.size()): internal_var_name = row.getVar(i).VarName if internal_var_name == self.name + '_aux': continue variable = self.problem._variables[internal_var_name] coeff = symbolics.Real(row.getCoeff(i)) terms.append(symbolics.mul((coeff, variable))) self._expression = symbolics.add(terms) return self._expression
def _get_expression(self): if self.problem is not None: cplex_problem = self.problem.problem try: cplex_row = cplex_problem.linear_constraints.get_rows(self.name) except CplexSolverError as e: if 'CPLEX Error 1219:' not in str(e): raise e else: cplex_row = cplex_problem.indicator_constraints.get_linear_components(self.name) variables = self.problem._variables expression = add( [mul((symbolics.Real(cplex_row.val[i]), variables[ind])) for i, ind in enumerate(cplex_row.ind)]) self._expression = expression return self._expression
def _get_expression(self): if self.problem is not None: cplex_problem = self.problem.problem try: cplex_row = cplex_problem.linear_constraints.get_rows(self.name) except CplexSolverError as e: if 'CPLEX Error 1219:' not in str(e): raise e else: cplex_row = cplex_problem.indicator_constraints.get_linear_components(self.name) variables = self.problem._variables expression = add( [mul((symbolics.Real(cplex_row.val[i]), variables[ind])) for i, ind in enumerate(cplex_row.ind)]) self._expression = expression return self._expression
def _get_quadratic_expression(self, quadratic=None): if quadratic is None: try: quadratic = self.problem.objective.get_quadratic() except IndexError: return Zero terms = [] for i, sparse_pair in enumerate(quadratic): for j, val in zip(sparse_pair.ind, sparse_pair.val): i_name, j_name = self.problem.variables.get_names([i, j]) if i < j: terms.append(val * self._variables[i_name] * self._variables[j_name]) elif i == j: terms.append(0.5 * val * self._variables[i_name] ** 2) else: pass # Only look at upper triangle return add(terms)
def _get_quadratic_expression(self, quadratic=None): if quadratic is None: try: quadratic = self.problem.objective.get_quadratic() except IndexError: return Zero terms = [] for i, sparse_pair in enumerate(quadratic): for j, val in zip(sparse_pair.ind, sparse_pair.val): i_name, j_name = self.problem.variables.get_names([i, j]) if i < j: terms.append(val * self._variables[i_name] * self._variables[j_name]) elif i == j: terms.append(0.5 * val * self._variables[i_name] ** 2) else: pass # Only look at upper triangle return add(terms)
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. """ constraints = [] for rxn in reactions: expression = add( [c * model.variables[m.id] for m, c in rxn.metabolites.items()]) constraints.append(Constraint(expression, lb=0, ub=0, name=rxn.id)) model.add(constraints)
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. """ constraints = [] for rxn in reactions: expression = add( [c * model.variables[m.id] for m, c in rxn.metabolites.items()]) constraints.append(Constraint(expression, lb=0, ub=0, name=rxn.id)) model.add(constraints)
def _get_expression(self): if (self.problem is not None and self._changed_expression and len(self.problem._variables) > 0): coeffs = self._expression.as_coefficients_dict() new_vars = set(self._changed_expression) - set(coeffs) self._expression += symbolics.add( [var * self._changed_expression[var] for var in new_vars]) # Substitute var in expression with var * coef / old_coef updates = { var: var * coef / coeffs[var] for var, coef in self._changed_expression.items() if var not in new_vars } self._expression = self._expression.subs(updates) self._changed_expression = {} return self._expression
def parse_expr(expr, local_dict=None): """ Parses a json-object created with 'expr_to_json' into a Sympy expression. If a local_dict argument is passed, symbols with be looked up by name, and a new symbol will be created only if the name is not in local_dict. """ if local_dict is None: local_dict = {} if expr["type"] == "Add": return add([parse_expr(arg, local_dict) for arg in expr["args"]]) elif expr["type"] == "Mul": return mul([parse_expr(arg, local_dict) for arg in expr["args"]]) elif expr["type"] == "Pow": return Pow(parse_expr(arg, local_dict) for arg in expr["args"]) elif expr["type"] == "Symbol": try: return local_dict[expr["name"]] except KeyError: return symbolics.Symbol(expr["name"]) elif expr["type"] == "Number": return symbolics.sympify(expr["value"]) else: raise NotImplementedError(expr["type"] + " is not implemented")
def parse_expr(expr, local_dict=None): """ Parses a json-object created with 'expr_to_json' into a Sympy expression. If a local_dict argument is passed, symbols with be looked up by name, and a new symbol will be created only if the name is not in local_dict. """ if local_dict is None: local_dict = {} if expr["type"] == "Add": return add([parse_expr(arg, local_dict) for arg in expr["args"]]) elif expr["type"] == "Mul": return mul([parse_expr(arg, local_dict) for arg in expr["args"]]) elif expr["type"] == "Pow": return Pow(parse_expr(arg, local_dict) for arg in expr["args"]) elif expr["type"] == "Symbol": try: return local_dict[expr["name"]] except KeyError: return symbolics.Symbol(expr["name"]) elif expr["type"] == "Number": return symbolics.sympify(expr["value"]) else: raise NotImplementedError(expr["type"] + " is not implemented")
def _initialize_model_from_problem(self, problem): try: self.problem = problem glp_create_index(self.problem) except TypeError: raise TypeError("Provided problem is not a valid GLPK model.") row_num = glp_get_num_rows(self.problem) col_num = glp_get_num_cols(self.problem) for i in range(1, col_num + 1): var = Variable( glp_get_col_name(self.problem, i), lb=glp_get_col_lb(self.problem, i), ub=glp_get_col_ub(self.problem, i), problem=self, type=_GLPK_VTYPE_TO_VTYPE[ glp_get_col_kind(self.problem, i)] ) # This avoids adding the variable to the glpk problem super(Model, self)._add_variables([var]) variables = self.variables for j in range(1, row_num + 1): ia = intArray(col_num + 1) da = doubleArray(col_num + 1) nnz = glp_get_mat_row(self.problem, j, ia, da) constraint_variables = [variables[ia[i] - 1] for i in range(1, nnz + 1)] # Since constraint expressions are lazily retrieved from the solver they don't have to be built here # lhs = _unevaluated_Add(*[da[i] * constraint_variables[i - 1] # for i in range(1, nnz + 1)]) lhs = 0 glpk_row_type = glp_get_row_type(self.problem, j) if glpk_row_type == GLP_FX: row_lb = glp_get_row_lb(self.problem, j) row_ub = row_lb elif glpk_row_type == GLP_LO: row_lb = glp_get_row_lb(self.problem, j) row_ub = None elif glpk_row_type == GLP_UP: row_lb = None row_ub = glp_get_row_ub(self.problem, j) elif glpk_row_type == GLP_DB: row_lb = glp_get_row_lb(self.problem, j) row_ub = glp_get_row_ub(self.problem, j) elif glpk_row_type == GLP_FR: row_lb = None row_ub = None else: raise Exception( "Currently, optlang does not support glpk row type %s" % str(glpk_row_type) ) log.exception() if isinstance(lhs, int): lhs = symbolics.Integer(lhs) elif isinstance(lhs, float): lhs = symbolics.Real(lhs) constraint_id = glp_get_row_name(self.problem, j) for variable in constraint_variables: try: self._variables_to_constraints_mapping[variable.name].add(constraint_id) except KeyError: self._variables_to_constraints_mapping[variable.name] = set([constraint_id]) super(Model, self)._add_constraints( [Constraint(lhs, lb=row_lb, ub=row_ub, name=constraint_id, problem=self, sloppy=True)], sloppy=True ) term_generator = ( (glp_get_obj_coef(self.problem, index), variables[index - 1]) for index in range(1, glp_get_num_cols(problem) + 1) ) self._objective = Objective( symbolics.add( [symbolics.mul((symbolics.Real(term[0]), term[1])) for term in term_generator if term[0] != 0.] ), problem=self, direction={GLP_MIN: 'min', GLP_MAX: 'max'}[glp_get_obj_dir(self.problem)]) glp_scale_prob(self.problem, GLP_SF_AUTO)
def __init__(self, problem=None, *args, **kwargs): super(Model, self).__init__(*args, **kwargs) if problem is None: self.problem = cplex.Cplex() elif isinstance(problem, cplex.Cplex): self.problem = problem zipped_var_args = zip(self.problem.variables.get_names(), self.problem.variables.get_lower_bounds(), self.problem.variables.get_upper_bounds(), # self.problem.variables.get_types(), # TODO uncomment when cplex is fixed ) for name, lb, ub in zipped_var_args: var = Variable(name, lb=lb, ub=ub, problem=self) # Type should also be in there super(Model, self)._add_variables([var]) # This avoids adding the variable to the glpk problem zipped_constr_args = zip(self.problem.linear_constraints.get_names(), self.problem.linear_constraints.get_rows(), self.problem.linear_constraints.get_senses(), self.problem.linear_constraints.get_rhs() ) variables = self._variables for name, row, sense, rhs in zipped_constr_args: constraint_variables = [variables[i - 1] for i in row.ind] # Since constraint expressions are lazily retrieved from the solver they don't have to be built here # lhs = _unevaluated_Add(*[val * variables[i - 1] for i, val in zip(row.ind, row.val)]) lhs = symbolics.Integer(0) if sense == 'E': constr = Constraint(lhs, lb=rhs, ub=rhs, name=name, problem=self) elif sense == 'G': constr = Constraint(lhs, lb=rhs, name=name, problem=self) elif sense == 'L': constr = Constraint(lhs, ub=rhs, name=name, problem=self) elif sense == 'R': range_val = self.problem.linear_constraints.get_range_values(name) if range_val > 0: constr = Constraint(lhs, lb=rhs, ub=rhs + range_val, name=name, problem=self) else: constr = Constraint(lhs, lb=rhs + range_val, ub=rhs, name=name, problem=self) else: # pragma: no cover raise Exception('%s is not a recognized constraint sense.' % sense) for variable in constraint_variables: try: self._variables_to_constraints_mapping[variable.name].add(name) except KeyError: self._variables_to_constraints_mapping[variable.name] = set([name]) super(Model, self)._add_constraints( [constr], sloppy=True ) try: objective_name = self.problem.objective.get_name() except CplexSolverError as e: if 'CPLEX Error 1219:' not in str(e): raise e else: linear_expression = add( [mul(symbolics.Real(coeff), variables[index]) for index, coeff in enumerate(self.problem.objective.get_linear()) if coeff != 0.] ) try: quadratic = self.problem.objective.get_quadratic() except IndexError: quadratic_expression = Zero else: quadratic_expression = self._get_quadratic_expression(quadratic) self._objective = Objective( linear_expression + quadratic_expression, problem=self, direction= {self.problem.objective.sense.minimize: 'min', self.problem.objective.sense.maximize: 'max'}[ self.problem.objective.get_sense()], name=objective_name ) else: raise TypeError("Provided problem is not a valid CPLEX model.") self.configuration = Configuration(problem=self, verbosity=0)
def __init__(self, problem=None, *args, **kwargs): super(Model, self).__init__(*args, **kwargs) if problem is None: self.problem = gurobipy.Model() self.problem.params.OutputFlag = 0 if self.name is not None: self.problem.setAttr('ModelName', self.name) self.problem.update() elif isinstance(problem, gurobipy.Model): self.problem = problem variables = [] for gurobi_variable in self.problem.getVars(): variables.append(Variable( gurobi_variable.getAttr("VarName"), lb=gurobi_variable.getAttr("lb"), ub=gurobi_variable.getAttr("ub"), problem=self, type=_GUROBI_VTYPE_TO_VTYPE[gurobi_variable.getAttr("vType")] )) super(Model, self)._add_variables(variables) constraints = [] for gurobi_constraint in self.problem.getConstrs(): sense = gurobi_constraint.Sense name = gurobi_constraint.getAttr("ConstrName") rhs = gurobi_constraint.RHS row = self.problem.getRow(gurobi_constraint) lhs = symbolics.add( [symbolics.Real(row.getCoeff(i)) * self.variables[row.getVar(i).VarName] for i in range(row.size())] ) if sense == '=': constraint = Constraint(lhs, name=name, lb=rhs, ub=rhs, problem=self) elif sense == '>': constraint = Constraint(lhs, name=name, lb=rhs, ub=None, problem=self) elif sense == '<': constraint = Constraint(lhs, name=name, lb=None, ub=rhs, problem=self) else: raise ValueError('{} is not a valid sense'.format(sense)) constraints.append(constraint) super(Model, self)._add_constraints(constraints, sloppy=True) gurobi_objective = self.problem.getObjective() if self.problem.IsQP: quadratic_expression = symbolics.add( [symbolics.Real(gurobi_objective.getCoeff(i)) * self.variables[gurobi_objective.getVar1(i).VarName] * self.variables[gurobi_objective.getVar2(i).VarName] for i in range(gurobi_objective.size())]) linear_objective = gurobi_objective.getLinExpr() else: quadratic_expression = symbolics.Real(0.0) linear_objective = gurobi_objective linear_expression = symbolics.add( [symbolics.Real(linear_objective.getCoeff(i)) * self.variables[linear_objective.getVar(i).VarName] for i in range(linear_objective.size())] ) self._objective = Objective( quadratic_expression + linear_expression, problem=self, direction={1: 'min', -1: 'max'}[self.problem.getAttr('ModelSense')] ) else: raise TypeError("Provided problem is not a valid Gurobi model.") self.configuration = Configuration(problem=self, verbosity=0)
def flux_mode_constraints(model, c=1): """ Add indicator constraints to allow the enumeration of k-shortest elementary flux modes, as described in [1]_. Parameters ---------- model: cobra.core.model Model for which to compute elementary flux modes c : float The minimum allowable flux through a given elementary flux mode. Defaults to 1. Yields ------ model The input model with additional constraints added. indicator_variables : dict A dictionary of optlang.Variable objects corresponding to the added indicator variables. """ with model: indicator_variables = dict() indicator_constraints = list() Variable = model.problem.Variable Constraint = model.problem.Constraint for reaction in model.reactions: if reaction.upper_bound > 0: fwd_id = 'y_fwd_' + reaction.id y_fwd = Variable(fwd_id, type='binary') indicator_variables[fwd_id] = y_fwd indicator_constraints += [ Constraint(reaction.forward_variable, indicator_variable=y_fwd, active_when=0, lb=0, ub=0, name='indicator_constraint_fwd_1_{}'.format( reaction.id)) ] indicator_constraints += [ Constraint(reaction.forward_variable, indicator_variable=y_fwd, active_when=1, lb=c, name='indicator_constraint_fwd_2_{}'.format( reaction.id)) ] # Only if y is reversible if reaction.lower_bound < 0: rev_id = 'y_rev_' + reaction.id y_rev = Variable(rev_id, type='binary') indicator_variables[rev_id] = y_rev indicator_constraints += [ Constraint(reaction.reverse_variable, indicator_variable=y_rev, active_when=0, lb=0, ub=0, name='indicator_constraint_rev_1_{}'.format( reaction.id)) ] indicator_constraints += [ Constraint(reaction.reverse_variable, indicator_variable=y_rev, active_when=1, lb=c, name='indicator_constraint_rev_2_{}'.format( reaction.id)) ] if reaction.reversibility: indicator_constraints += [ Constraint(y_fwd + y_rev, lb=0, ub=1, name='one_direction_constraint_{}'.format( reaction.id)) ] indicator_constraints += [ Constraint(add(*indicator_variables.values()), lb=1, name='an_EM_must_constain_at_least_one_active_reaction') ] model.add_cons_vars( list(indicator_variables.values()) + indicator_constraints) model.objective = add(*indicator_variables.values()) model.objective_direction = 'min' model._indicator_variables = indicator_variables yield model # Clean up additional model attribute del model._indicator_variables
def add_moma(model, solution=None, linear=True): r"""Add constraints and objective representing for MOMA. This adds variables and constraints for the minimization of metabolic adjustment (MOMA) to the model. Parameters ---------- model : cobra.Model The model to add MOMA constraints and objective to. solution : cobra.Solution, optional A previous solution to use as a reference. If no solution is given, one will be computed using pFBA. linear : bool, optional Whether to use the linear MOMA formulation or not (default True). Notes ----- In the original MOMA [1]_ specification one looks for the flux distribution of the deletion (v^d) closest to the fluxes without the deletion (v). In math this means: minimize \sum_i (v^d_i - v_i)^2 s.t. Sv^d = 0 lb_i <= v^d_i <= ub_i Here, we use a variable transformation v^t := v^d_i - v_i. Substituting and using the fact that Sv = 0 gives: minimize \sum_i (v^t_i)^2 s.t. Sv^d = 0 v^t = v^d_i - v_i lb_i <= v^d_i <= ub_i So basically we just re-center the flux space at the old solution and then find the flux distribution closest to the new zero (center). This is the same strategy as used in cameo. In the case of linear MOMA [2]_, we instead minimize \sum_i abs(v^t_i). The linear MOMA is typically significantly faster. Also quadratic MOMA tends to give flux distributions in which all fluxes deviate from the reference fluxes a little bit whereas linear MOMA tends to give flux distributions where the majority of fluxes are the same reference with few fluxes deviating a lot (typical effect of L2 norm vs L1 norm). The former objective function is saved in the optlang solver interface as ``"moma_old_objective"`` and this can be used to immediately extract the value of the former objective after MOMA optimization. See Also -------- pfba : parsimonious FBA References ---------- .. [1] Segrè, Daniel, Dennis Vitkup, and George M. Church. “Analysis of Optimality in Natural and Perturbed Metabolic Networks.” Proceedings of the National Academy of Sciences 99, no. 23 (November 12, 2002): 15112. https://doi.org/10.1073/pnas.232349399. .. [2] Becker, Scott A, Adam M Feist, Monica L Mo, Gregory Hannum, Bernhard Ø Palsson, and Markus J Herrgard. “Quantitative Prediction of Cellular Metabolism with Constraint-Based Models: The COBRA Toolbox.” Nature Protocols 2 (March 29, 2007): 727. """ if 'moma_old_objective' in model.solver.variables: raise ValueError('model is already adjusted for MOMA') # Fall back to default QP solver if current one has no QP capability if not linear: model.solver = sutil.choose_solver(model, qp=True) if solution is None: solution = pfba(model) prob = model.problem v = prob.Variable("moma_old_objective") c = prob.Constraint(model.solver.objective.expression - v, lb=0.0, ub=0.0, name="moma_old_objective_constraint") to_add = [v, c] model.objective = prob.Objective(Zero, direction="min", sloppy=True) obj_vars = [] for r in model.reactions: flux = solution.fluxes[r.id] if linear: components = sutil.add_absolute_expression(model, r.flux_expression, name="moma_dist_" + r.id, difference=flux, add=False) to_add.extend(components) obj_vars.append(components.variable) else: dist = prob.Variable("moma_dist_" + r.id) const = prob.Constraint(r.flux_expression - dist, lb=flux, ub=flux, name="moma_constraint_" + r.id) to_add.extend([dist, const]) obj_vars.append(dist**2) model.add_cons_vars(to_add) if linear: model.objective.set_linear_coefficients({v: 1.0 for v in obj_vars}) else: model.objective = prob.Objective(add(obj_vars), direction="min", sloppy=True)
def to_symbolic_expr(coeffs): """Converts coeffs dict to symbolic expression.""" return symbolics.add([ var * coef for var, coef in coeffs.items() if not isclose(0, to_float(coef)) ])
def _initialize_model_from_problem(self, problem): if isinstance(problem, gurobipy.Model): self.problem = problem variables = [] for gurobi_variable in self.problem.getVars(): variables.append( Variable(gurobi_variable.getAttr("VarName"), lb=gurobi_variable.getAttr("lb"), ub=gurobi_variable.getAttr("ub"), problem=self, type=_GUROBI_VTYPE_TO_VTYPE[ gurobi_variable.getAttr("vType")])) super(Model, self)._add_variables(variables) constraints = [] for gurobi_constraint in self.problem.getConstrs(): sense = gurobi_constraint.Sense name = gurobi_constraint.getAttr("ConstrName") rhs = gurobi_constraint.RHS row = self.problem.getRow(gurobi_constraint) lhs = symbolics.add([ symbolics.Real(row.getCoeff(i)) * self.variables[row.getVar(i).VarName] for i in range(row.size()) ]) if sense == '=': constraint = Constraint(lhs, name=name, lb=rhs, ub=rhs, problem=self) elif sense == '>': constraint = Constraint(lhs, name=name, lb=rhs, ub=None, problem=self) elif sense == '<': constraint = Constraint(lhs, name=name, lb=None, ub=rhs, problem=self) else: raise ValueError('{} is not a valid sense'.format(sense)) constraints.append(constraint) super(Model, self)._add_constraints(constraints, sloppy=True) gurobi_objective = self.problem.getObjective() if self.problem.IsQP: quadratic_expression = symbolics.add([ symbolics.Real(gurobi_objective.getCoeff(i)) * self.variables[gurobi_objective.getVar1(i).VarName] * self.variables[gurobi_objective.getVar2(i).VarName] for i in range(gurobi_objective.size()) ]) linear_objective = gurobi_objective.getLinExpr() else: quadratic_expression = symbolics.Real(0.0) linear_objective = gurobi_objective linear_expression = symbolics.add([ symbolics.Real(linear_objective.getCoeff(i)) * self.variables[linear_objective.getVar(i).VarName] for i in range(linear_objective.size()) ]) self._objective = Objective(quadratic_expression + linear_expression, problem=self, direction={ 1: 'min', -1: 'max' }[self.problem.getAttr('ModelSense')]) else: raise TypeError("Provided problem is not a valid Gurobi model.")
def _initialize_model_from_problem(self, problem, vc_mapping=None, offset=0): if not isinstance(problem, OSQPProblem): raise TypeError("Provided problem is not a valid OSQP model.") self.problem = problem for name in self.problem.variables: var = Variable( name, lb=self.problem.variable_lbs[name], ub=self.problem.variable_ubs[name], problem=self, ) super(Model, self)._add_variables([var]) for name in self.problem.constraints: # Since constraint expressions are lazily retrieved from the # solver they don't have to be built here lhs = symbolics.Integer(0) constr = Constraint( lhs, lb=self.problem.constraint_lbs[name], ub=self.problem.constraint_ubs[name], name=name, problem=self, ) super(Model, self)._add_constraints([constr], sloppy=True) if vc_mapping is None: for constr in self.constraints: name = constr.name for variable in constr.variables: try: self._variables_to_constraints_mapping[ variable.name].add(name) except KeyError: self._variables_to_constraints_mapping[ variable.name] = set([name]) else: self._variables_to_constraints_mapping = vc_mapping linear_expression = add([ coef * self._variables[vn] for vn, coef in six.iteritems(self.problem.obj_linear_coefs) ]) quadratic_expression = add([ coef * self._variables[vn[0]] * self._variables[vn[1]] for vn, coef in six.iteritems(self.problem.obj_quadratic_coefs) ]) self._objective_offset = offset self._objective = Objective( linear_expression + quadratic_expression + offset, problem=self, direction={ -1: "max", 1: "min" }[self.problem.direction], name="osqp_objective", )
def set_objective(self, linear=None, quadratic=None, minimize=True): """ Set a predefined objective for this problem. Args: linear (dict): linear coefficients (optional) quadratic (dict): quadratic coefficients (optional) minimize (bool): solve a minimization problem (default: True) Notes: Setting the objective is optional. It can also be passed directly when calling **solve**. """ if linear is None: linear = {} if quadratic is None: quadratic = {} if linear and not quadratic: objective = {} if isinstance(linear, str): objective = {self.problem.variables[linear]: 1} if linear not in self.var_ids: warn( f"Objective variable not previously declared: {linear}" ) else: for r_id, val in linear.items(): if r_id not in self.var_ids: warn( f"Objective variable not previously declared: {r_id}" ) elif val != 0: objective[self.problem.variables[r_id]] = val self.problem.objective = Objective( Zero, direction=('min' if minimize else 'max'), sloppy=True) self.problem.objective.set_linear_coefficients(objective) else: objective = [] for r_id, val in linear.items(): if r_id not in self.var_ids: warn(f"Objective variable not previously declared: {r_id}") elif val != 0: objective.append(val * self.problem.variables[r_id]) for (r_id1, r_id2), val in quadratic.items(): if r_id1 not in self.var_ids: warn( f"Objective variable not previously declared: {r_id1}") elif r_id2 not in self.var_ids: warn( f"Objective variable not previously declared: {r_id2}") elif val != 0: objective.append(val * self.problem.variables[r_id1] * self.problem.variables[r_id2]) objective_expr = add(objective) self.problem.objective = Objective( objective_expr, direction=('min' if minimize else 'max'), sloppy=True)
def __init__(self, problem=None, *args, **kwargs): super(Model, self).__init__(*args, **kwargs) self.configuration = Configuration() if problem is None: self.problem = glp_create_prob() glp_create_index(self.problem) if self.name is not None: _glpk_validate_id(self.name) glp_set_prob_name(self.problem, str(self.name)) else: try: self.problem = problem glp_create_index(self.problem) except TypeError: raise TypeError("Provided problem is not a valid GLPK model.") row_num = glp_get_num_rows(self.problem) col_num = glp_get_num_cols(self.problem) for i in range(1, col_num + 1): var = Variable( glp_get_col_name(self.problem, i), lb=glp_get_col_lb(self.problem, i), ub=glp_get_col_ub(self.problem, i), problem=self, type=_GLPK_VTYPE_TO_VTYPE[ glp_get_col_kind(self.problem, i)] ) # This avoids adding the variable to the glpk problem super(Model, self)._add_variables([var]) variables = self.variables for j in range(1, row_num + 1): ia = intArray(col_num + 1) da = doubleArray(col_num + 1) nnz = glp_get_mat_row(self.problem, j, ia, da) constraint_variables = [variables[ia[i] - 1] for i in range(1, nnz + 1)] # Since constraint expressions are lazily retrieved from the solver they don't have to be built here # lhs = _unevaluated_Add(*[da[i] * constraint_variables[i - 1] # for i in range(1, nnz + 1)]) lhs = 0 glpk_row_type = glp_get_row_type(self.problem, j) if glpk_row_type == GLP_FX: row_lb = glp_get_row_lb(self.problem, j) row_ub = row_lb elif glpk_row_type == GLP_LO: row_lb = glp_get_row_lb(self.problem, j) row_ub = None elif glpk_row_type == GLP_UP: row_lb = None row_ub = glp_get_row_ub(self.problem, j) elif glpk_row_type == GLP_DB: row_lb = glp_get_row_lb(self.problem, j) row_ub = glp_get_row_ub(self.problem, j) elif glpk_row_type == GLP_FR: row_lb = None row_ub = None else: raise Exception( "Currently, optlang does not support glpk row type %s" % str(glpk_row_type) ) log.exception() if isinstance(lhs, int): lhs = symbolics.Integer(lhs) elif isinstance(lhs, float): lhs = symbolics.Real(lhs) constraint_id = glp_get_row_name(self.problem, j) for variable in constraint_variables: try: self._variables_to_constraints_mapping[variable.name].add(constraint_id) except KeyError: self._variables_to_constraints_mapping[variable.name] = set([constraint_id]) super(Model, self)._add_constraints( [Constraint(lhs, lb=row_lb, ub=row_ub, name=constraint_id, problem=self, sloppy=True)], sloppy=True ) term_generator = ( (glp_get_obj_coef(self.problem, index), variables[index - 1]) for index in range(1, glp_get_num_cols(problem) + 1) ) self._objective = Objective( symbolics.add( [symbolics.mul((symbolics.Real(term[0]), term[1])) for term in term_generator if term[0] != 0.] ), problem=self, direction={GLP_MIN: 'min', GLP_MAX: 'max'}[glp_get_obj_dir(self.problem)]) glp_scale_prob(self.problem, GLP_SF_AUTO)
def __init__(self, problem=None, *args, **kwargs): super(Model, self).__init__(*args, **kwargs) if problem is None: self.problem = cplex.Cplex() elif isinstance(problem, cplex.Cplex): self.problem = problem zipped_var_args = zip( self.problem.variables.get_names(), self.problem.variables.get_lower_bounds(), self.problem.variables.get_upper_bounds(), # self.problem.variables.get_types(), # TODO uncomment when cplex is fixed ) for name, lb, ub in zipped_var_args: var = Variable(name, lb=lb, ub=ub, problem=self) # Type should also be in there super(Model, self)._add_variables([ var ]) # This avoids adding the variable to the glpk problem zipped_constr_args = zip( self.problem.linear_constraints.get_names(), self.problem.linear_constraints.get_rows(), self.problem.linear_constraints.get_senses(), self.problem.linear_constraints.get_rhs()) variables = self._variables for name, row, sense, rhs in zipped_constr_args: constraint_variables = [variables[i - 1] for i in row.ind] # Since constraint expressions are lazily retrieved from the solver they don't have to be built here # lhs = _unevaluated_Add(*[val * variables[i - 1] for i, val in zip(row.ind, row.val)]) lhs = symbolics.Integer(0) if sense == 'E': constr = Constraint(lhs, lb=rhs, ub=rhs, name=name, problem=self) elif sense == 'G': constr = Constraint(lhs, lb=rhs, name=name, problem=self) elif sense == 'L': constr = Constraint(lhs, ub=rhs, name=name, problem=self) elif sense == 'R': range_val = self.problem.linear_constraints.get_range_values( name) if range_val > 0: constr = Constraint(lhs, lb=rhs, ub=rhs + range_val, name=name, problem=self) else: constr = Constraint(lhs, lb=rhs + range_val, ub=rhs, name=name, problem=self) else: # pragma: no cover raise Exception( '%s is not a recognized constraint sense.' % sense) for variable in constraint_variables: try: self._variables_to_constraints_mapping[ variable.name].add(name) except KeyError: self._variables_to_constraints_mapping[ variable.name] = set([name]) super(Model, self)._add_constraints([constr], sloppy=True) try: objective_name = self.problem.objective.get_name() except CplexSolverError as e: if 'CPLEX Error 1219:' not in str(e): raise e else: linear_expression = add([ mul(symbolics.Real(coeff), variables[index]) for index, coeff in enumerate(self.problem.objective.get_linear()) if coeff != 0. ]) try: quadratic = self.problem.objective.get_quadratic() except IndexError: quadratic_expression = Zero else: quadratic_expression = self._get_quadratic_expression( quadratic) self._objective = Objective( linear_expression + quadratic_expression, problem=self, direction={ self.problem.objective.sense.minimize: 'min', self.problem.objective.sense.maximize: 'max' }[self.problem.objective.get_sense()], name=objective_name) else: raise TypeError("Provided problem is not a valid CPLEX model.") self.configuration = Configuration(problem=self, verbosity=0)
def add_moma(model, solution=None, linear=True): r"""Add constraints and objective representing for MOMA. This adds variables and constraints for the minimization of metabolic adjustment (MOMA) to the model. Parameters ---------- model : cobra.Model The model to add MOMA constraints and objective to. solution : cobra.Solution, optional A previous solution to use as a reference. If no solution is given, one will be computed using pFBA. linear : bool, optional Whether to use the linear MOMA formulation or not (default True). Notes ----- In the original MOMA [1]_ specification one looks for the flux distribution of the deletion (v^d) closest to the fluxes without the deletion (v). In math this means: minimize \sum_i (v^d_i - v_i)^2 s.t. Sv^d = 0 lb_i <= v^d_i <= ub_i Here, we use a variable transformation v^t := v^d_i - v_i. Substituting and using the fact that Sv = 0 gives: minimize \sum_i (v^t_i)^2 s.t. Sv^d = 0 v^t = v^d_i - v_i lb_i <= v^d_i <= ub_i So basically we just re-center the flux space at the old solution and then find the flux distribution closest to the new zero (center). This is the same strategy as used in cameo. In the case of linear MOMA [2]_, we instead minimize \sum_i abs(v^t_i). The linear MOMA is typically significantly faster. Also quadratic MOMA tends to give flux distributions in which all fluxes deviate from the reference fluxes a little bit whereas linear MOMA tends to give flux distributions where the majority of fluxes are the same reference with few fluxes deviating a lot (typical effect of L2 norm vs L1 norm). The former objective function is saved in the optlang solver interface as ``"moma_old_objective"`` and this can be used to immediately extract the value of the former objective after MOMA optimization. See Also -------- pfba : parsimonious FBA References ---------- .. [1] Segrè, Daniel, Dennis Vitkup, and George M. Church. “Analysis of Optimality in Natural and Perturbed Metabolic Networks.” Proceedings of the National Academy of Sciences 99, no. 23 (November 12, 2002): 15112. https://doi.org/10.1073/pnas.232349399. .. [2] Becker, Scott A, Adam M Feist, Monica L Mo, Gregory Hannum, Bernhard Ø Palsson, and Markus J Herrgard. “Quantitative Prediction of Cellular Metabolism with Constraint-Based Models: The COBRA Toolbox.” Nature Protocols 2 (March 29, 2007): 727. """ if 'moma_old_objective' in model.solver.variables: raise ValueError('model is already adjusted for MOMA') # Fall back to default QP solver if current one has no QP capability if not linear: model.solver = sutil.choose_solver(model, qp=True) if solution is None: solution = pfba(model) prob = model.problem v = prob.Variable("moma_old_objective") c = prob.Constraint(model.solver.objective.expression - v, lb=0.0, ub=0.0, name="moma_old_objective_constraint") to_add = [v, c] model.objective = prob.Objective(Zero, direction="min", sloppy=True) obj_vars = [] for r in model.reactions: flux = solution.fluxes[r.id] if linear: components = sutil.add_absolute_expression( model, r.flux_expression, name="moma_dist_" + r.id, difference=flux, add=False) to_add.extend(components) obj_vars.append(components.variable) else: dist = prob.Variable("moma_dist_" + r.id) const = prob.Constraint(r.flux_expression - dist, lb=flux, ub=flux, name="moma_constraint_" + r.id) to_add.extend([dist, const]) obj_vars.append(dist ** 2) model.add_cons_vars(to_add) if linear: model.objective.set_linear_coefficients({v: 1.0 for v in obj_vars}) else: model.objective = prob.Objective( add(obj_vars), direction="min", sloppy=True)