def __init__(self, model=None): Solver.__init__(self) self.problem = Cplex() self.status_mapping = { self.problem.solution.status.optimal: Status.OPTIMAL, self.problem.solution.status.optimal_tolerance: Status.OPTIMAL, self.problem.solution.status.unbounded: Status.UNBOUNDED, self.problem.solution.status.infeasible: Status.INFEASIBLE, self.problem.solution.status.infeasible_or_unbounded: Status.INF_OR_UNB, self.problem.solution.status.MIP_optimal: Status.OPTIMAL, self.problem.solution.status.MIP_unbounded: Status.UNBOUNDED, self.problem.solution.status.MIP_infeasible: Status.INFEASIBLE, self.problem.solution.status.MIP_infeasible_or_unbounded: Status.INF_OR_UNB } self.vartype_mapping = { VarType.BINARY: self.problem.variables.type.binary, VarType.INTEGER: self.problem.variables.type.integer, VarType.CONTINUOUS: self.problem.variables.type.continuous } self.parameter_mapping = { Parameter.TIME_LIMIT: self.problem.parameters.timelimit, Parameter.FEASIBILITY_TOL: self.problem.parameters.simplex.tolerances.feasibility, Parameter.OPTIMALITY_TOL: self.problem.parameters.simplex.tolerances.optimality, Parameter.INT_FEASIBILITY_TOL: self.problem.parameters.mip.tolerances.integrality, Parameter.MIP_ABS_GAP: self.problem.parameters.mip.tolerances.mipgap, Parameter.MIP_REL_GAP: self.problem.parameters.mip.tolerances.absmipgap, Parameter.POOL_SIZE: self.problem.parameters.mip.limits.populate, Parameter.POOL_GAP: self.problem.parameters.mip.pool.relgap } self.set_parameters(default_parameters) self.set_logging(False) self._cached_lin_obj = {} self._cached_sense = None self._cached_lower_bounds = {} self._cached_upper_bounds = {} self._cached_vars = [] self._cached_constrs = [] if model: self.build_problem(model)
def solve(problem: cplex.Cplex, dataset: Dataset): global best_solution, best_score # Ищем решение с текущими ограничениями problem.solve() # Если решение не удовлетворяет ограничениям, не идем в эту ветку if (problem.solution.get_status() is not 1): print("Broken model") return None obj = problem.solution.get_objective_value() # Если решение хуже лучшего, не идем в эту ветку if (obj <= best_score): return None prev_ans = obj add_constr = lambda constraint: problem.linear_constraints.add( lin_expr= [cplex.SparsePair(constraint, val=[1.0] * len(constraint))], rhs=[1.0], names=["c_" + str(np.random.randint(low=10, high=1000000))], senses=['L']) while True: constraint = BranchAndCut.separation(dataset, problem.solution.get_values()) if (len(constraint) <= 1): break add_constr(constraint) problem.solve() obj = problem.solution.get_objective_value() if (obj < best_score): return None elif np.isclose(prev_ans, obj, atol=10e-6): break prev_ans = obj solution = problem.solution.get_values() if BranchAndCut.validate_integer_solution(solution): constraints = BranchAndCut.check_solution(problem, dataset) if len(constraints) == 0: if obj > best_score: best_solution = solution best_score = obj print("New best result: ", best_score) else: for constr in constraints: add_constr(constr) BranchAndCut.solve(problem, dataset) return branching_var = BranchAndCut.get_branching_var(solution) branching_var = [int(branching_var)] for val in [1.0, 0.0]: constr = problem.linear_constraints.add( lin_expr=[cplex.SparsePair(branching_var, val=[1.0])], rhs=[val], names=["e_" + str(np.random.randint(low=10, high=1000000))], senses=['E']) BranchAndCut.solve(problem, dataset) problem.linear_constraints.delete(constr)
def __init__(self, cplex=None): if cplex: self._model = Cplex(cplex._model) else: self._model = Cplex() self._init_lin() # to avoid a variable with index 0 self._model.variables.add( names=['_dummy_'], types=[self._model.variables.type.continuous]) self._var_id = {'_dummy_': 0}
def __init__(self, problem): #instance variable: the solution set found by solver self.cplexSolutionSet = [] #instance variable: the solution map, the key is the solution obj values, the value is the solution self.cplexResultMap = {} #instance variable: the map of the solutions in the pareto front self.cplexParetoSet = {} # problem self.problem = problem # solver self.solver = Cplex()
def create_cplex(cls, verbose=False): cpx = Cplex() # see if we want it to be verbose sometimes if not verbose: cpx.set_log_stream(None) cpx.set_results_stream(None) cpx.set_error_stream(None) cpx.set_warning_stream(None) # disable datacheck cpx_datacheck_id = 1056 setintparam(cpx._env._e, cpx_datacheck_id, 0) return cpx
def __init__(self, cplex=None): try: if cplex: self._model = Cplex(cplex._model) else: self._model = Cplex() except NameError: raise NameError('CPLEX is not installed. See https://www.ibm.com/support/knowledgecenter/SSSA5P_12.8.0/ilog.odms.studio.help/Optimization_Studio/topics/COS_home.html') self._init_lin() # to avoid a variable with index 0 self._model.variables.add(names=['_dummy_'], types=[self._model.variables.type.continuous]) self._var_id = {'_dummy_': 0}
def set_cplex_objective(cpx: cplex.Cplex, c, Q=None, epsilon: float = 1e-4) -> cplex.Cplex: """ set_cplex_objective(cpx, c, Q) sets the objective of cplex object cpx to have linear objective coefficients c and quadratic objective coefficients Q essentially sets problem to have objective of the form min cTx + xT Q x with original constraints etc :param cpx: cplex object to modify :param c: linear objective coefficients :param Q: quadratic objective coefficients :return: cplex object with set objective coefficients """ n = cpx.variables.get_num() assert len(c) == n, "c must have {} items but len(c) is {}".format( n, len(c)) # set linear coefficients # cTx for i in range(n): cpx.objective.set_linear(i, float(c[i])) if Q is not None and len(Q.nonzero()[0]) > 0: assert len(Q) == n, "Q must have {} items but len(Q) is {}".format( n, len(Q)) # quadratic coefficients # x.T * Q * x quadratic_coefs = [] for i in range(Q.shape[0]): indices = Q[i].nonzero()[0].tolist() if len(indices) == 0: sparse_pair = cplex.SparsePair(ind=[], val=[]) else: vals = Q[i, indices].tolist() sparse_pair = cplex.SparsePair(ind=indices, val=vals) quadratic_coefs.append(sparse_pair) cpx.objective.set_quadratic(quadratic_coefs) else: cpx.set_problem_type(cpx.problem_type.MILP) cpx.objective.set_sense(cpx.objective.sense.minimize) cpx.cleanup(epsilon=epsilon) return cpx
def copy_cplex(cpx): cpx_copy = Cplex(cpx) cpx_parameters = cpx.parameters.get_changed() for (pname, pvalue) in cpx_parameters: phandle = reduce(getattr, str(pname).split("."), cpx_copy) phandle.set(pvalue) return cpx_copy
def __init__(self, cplex=None): if not _HAS_CPLEX: raise MissingOptionalLibraryError( libname='CPLEX', name='SimpleCPLEX', pip_install='pip install qiskit-aqua[cplex]') if cplex: self._model = Cplex(cplex._model) else: self._model = Cplex() self._init_lin() # to avoid a variable with index 0 self._model.variables.add(names=['_dummy_'], types=[self._model.variables.type.continuous]) self._var_id = {'_dummy_': 0}
def branching(self, problem: cplex.Cplex): try: problem.solve() solution_values = problem.solution.get_values() except cplex.exceptions.CplexSolverError: return list() if sum(solution_values) > self.current_max_clique_size: branching_variable = self.get_branching_variable(solution_values) if branching_variable is None: current_clique = list(index for index, value in enumerate(solution_values) if value == 1.0) self.current_max_clique_size = len(current_clique) if self.current_max_clique_size \ < len(current_clique)else self.current_max_clique_size return current_clique return max(self.branching(self.add_constraint(cplex.Cplex(problem), branching_variable, 1.0)), self.branching(self.add_constraint(cplex.Cplex(problem), branching_variable, 0.0)), key=lambda list: len(list)) return list()
def __init__(self, A): self.upper_bound = 1000 self.eps = 1e-10 self.dimension = 0 try: self.cpl = Cplex() except NameError, CplexSolverError: raise CplexNotInstalledError()
def lp(a, b, c): # number of equations (m) and unknowns (n) m, n = a.shape # objective function and lower bounds obj = c.copy() lb = zeros(n) # constraints count = 0 sense = "" rows = [] cols = [] vals = [] rhs = [] for i in range(m): rows.extend([count for k in range(n)]) cols.extend([k for k in range(n)]) vals.extend(a[i, :]) rhs.append(b[i]) sense += "L" count += 1 # cplex problem variable prob = Cplex() # quiet results #prob.set_results_stream(None) # maximiation problem prob.objective.set_sense(prob.objective.sense.maximize) # problem variables prob.variables.add(obj=obj, lb=lb) #for j in range(prob.variables.get_num()): # prob.variables.set_types(j,prob.variables.type.integer) # linear constraints prob.linear_constraints.add(rhs=rhs, senses=sense) prob.linear_constraints.set_coefficients(zip(rows, cols, vals)) # alg method alg = prob.parameters.lpmethod.values prob.parameters.lpmethod.set(alg.auto) # solve problem prob.solve() # solution variables var = prob.solution.get_values() x = var[0:n] opt = prob.solution.get_objective_value() # return return opt, x
def modelisation(): # Variables m, L, li, qi = read_instance_file(argv[1]) # Modèle pour résoudre le problème de minimisation des planches utilisées model = Cplex() model.set_results_stream(None) # Variables de décision du modèle model_variables = list(range(len(li))) model.variables.add(obj=[1 for j in model_variables]) # Contraintes du modèle model_contraintes = range(len(qi)) model.linear_constraints.add( lin_expr=[SparsePair() for j in model_contraintes], senses=["G" for j in model_contraintes], rhs=qi) for var_index in model_variables: model.linear_constraints.set_coefficients(var_index, var_index, int(L / li[var_index])) # Modèle utilisé pour générer des pattern en utilisant # la méthode Column generation de Gilmore-Gomory pattern_model = Cplex() pattern_model.set_results_stream(None) # Variable de décision panneaux_indices = range(len(li)) pattern_model.variables.add( types=[pattern_model.variables.type.integer for j in panneaux_indices]) pattern_model.variables.add(obj=[1], lb=[1], ub=[1]) # L'unique contrainte ici est que la taille total des panneaux ne peut être # plus grande que la longueur de la planche L. pattern_model.linear_constraints.add( lin_expr=[SparsePair(ind=panneaux_indices, val=li)], senses=["L"], rhs=[L]) # Définir l'objectif (Minimisation) pattern_model.objective.set_sense(pattern_model.objective.sense.minimize) return m, model, pattern_model, model_contraintes, model_variables, panneaux_indices
def formulate(self): self.set_aux() self.M = Cplex() if self.solver == 'cplex' else Model() self.addVars() self.addCons() self.setObj() self.p, self.a, self.obj, self.converged, self.tot_its = None,None,None,False,0 if self.verbose: print 'solving with', self.solver print '%2s %10s %8s %4s %3s %10s' % ('it', 'worst line', 'viol', 'stat', 'its', 'obj')
def select_winners(ads, max_duration): ''' Computes the set of winners that has the maximum total price. The numbers of winners are returned. ''' from cplex import Cplex prob = Cplex() prob.variables.add([ad['bid'] for ad in ads], types=prob.variables.type.binary * len(ads)) prob.linear_constraints.add([[range(len(ads)), [ad['duration'] for ad in ads]]], 'L', [max_duration]) prob.objective.set_sense(prob.objective.sense.maximize) prob.solve() winners = set() losers = set() sol = prob.solution.get_values() print sol for i, ad in enumerate(ads): if sol[i] > 0.5: winners.add(ad) else: losers.add(ad) return winners, losers
def copy_cplex(cpx): """ Copy a Cplex object :param cpx: Cplex object :return: Copy of Cplex object """ cpx_copy = Cplex(cpx) cpx_parameters = cpx.parameters.get_changed() for (pname, pvalue) in cpx_parameters: phandle = reduce(getattr, str(pname).split("."), cpx_copy) phandle.set(pvalue) return cpx_copy
def branch_and_bound(model: cplex.Cplex, optimal_objective_value, optimal_values): if check_timeout(): global is_timeout is_timeout = True return optimal_objective_value, optimal_values model.solve() new_result = model.solution.get_objective_value() new_variables = model.solution.get_values() integer_new_result = round_with_eps(new_result) if not is_result_improved(optimal_objective_value, integer_new_result): # print("BOUND:\tSolution became worse from:\t{}\tto\t{}".format(optimal_objective_value, new_result)) return optimal_objective_value, optimal_values if is_result_integer(new_variables): print("\nFound better integer solution:\tmax clique size:\t{}\n".format(new_result)) return integer_new_result, new_variables branching_var = branching(new_variables) # print("BRANCHING:\t\tmax clique float size:\t{}\t\t\t\t\t\t{}".format(new_result, branching_var)) up, down = build_new_constrains(branching_var) add_constraint(up, model) optimal_objective_value, optimal_values = branch_and_bound(model, optimal_objective_value, optimal_values) delete_constraint(up['names'][0], model) add_constraint(down, model) optimal_objective_value, optimal_values = branch_and_bound(model, optimal_objective_value, optimal_values) delete_constraint(down['names'][0], model) return optimal_objective_value, optimal_values
def __init_optimization_problem(self): problem = Cplex() sense = problem.objective.sense problem.objective.set_sense(sense=sense.maximize) variables = self.__build_variables() # self.__log('Variables: ', variables) objective = self.__build_objective(variables) # self.__log('Objective: ', objective) problem.variables.add(names=variables, types=['C'] * len(variables), ub=[1.0] * self.__problem.vertices_num(), obj=objective) constraints = self.__build_constraints(variables) # self.__log('Constraints count: ', len(constraints)) self.__set_constraints(problem, constraints) self.__optimization_problem = problem
def run_docplex_check_list(): check_platform() # check requirements check_import("six") check_import("enum") check_import("cloudpickle") # check cplex try: from cplex import Cplex cpx = Cplex() del cpx except ImportError: print("Cplex DLL not found, if present , you must add it to PYTHNPATH") # check pandas try: import pandas as pd from pandas import DataFrame, Series dd = DataFrame({}) except ImportError: print("-- pandas is not present, some features might be unavailable.")
class SimpleCPLEX: def __init__(self, cplex=None): if cplex: self._model = Cplex(cplex._model) else: self._model = Cplex() self._init_lin() # to avoid a variable with index 0 self._model.variables.add( names=['_dummy_'], types=[self._model.variables.type.continuous]) self._var_id = {'_dummy_': 0} def _init_lin(self): self._lin = { 'lin_expr': [], 'senses': [], 'rhs': [], 'range_values': [], 'names': [] } def register_variables(self, prefix, ranges, var_type, lb=None, ub=None): if not ranges: # None or [] return self._register_variable(prefix, var_type, lb, ub) variables = {} for keys in product(*ranges): name = '_'.join([prefix] + [str(e) for e in keys]) index = self._register_variable(name, var_type, lb, ub) if len(keys) == 1: keys = keys[0] variables[keys] = index return variables def _register_variable(self, name, var_type, lb, ub): self._model.variables.add(names=[name], types=[var_type], lb=[] if lb is None else [lb], ub=[] if ub is None else [ub]) if name in self._var_id: logger.info('Variable %s is already registered. Overwritten', name) index = len(self._var_id) self._var_id[name] = index return index def model(self): return self._model @property def parameters(self): return self._model.parameters @property def variables(self): return self._model.variables @property def objective(self): return self._model.objective @property def problem_type(self): return self._model.problem_type @property def solution(self): return self._model.solution @property def version(self): return self._model.get_version() def maximize(self): self._model.objective.set_sense(self._model.objective.sense.maximize) def minimize(self): self._model.objective.set_sense(self._model.objective.sense.minimize) def set_problem_type(self, problem_type): self._model.set_problem_type(problem_type) def tune_problem(self, options): self._model.set_results_stream(None) self._model.parameters.tune_problem(options) self._model.set_results_stream(stdout) def solve(self): self._model.solve() def populate(self): self._model.solve() self._model.populate_solution_pool() def variable(self, name): """ :param name: variable name :type name: str :return: variable index in CPLEX model :rtype: int """ return self._var_id[name] def get_values(self, lst, idx=None): if idx: return self._model.solution.pool.get_values(idx, lst) else: return self._model.solution.get_values(lst) def get_objective_value(self, idx=None): if idx: return self._model.solution.pool.get_objective_value(idx) else: return self._model.solution.get_objective_value() @property def num_solutions(self): return self._model.solution.pool.get_num() @staticmethod def _convert_sense(sense): # Note: ignore 'R' range case assert sense in ['E', 'L', 'G', '>=', '=', '==', '<='] if sense == '<=': sense = 'L' elif sense == '=' or sense == '==': sense = 'E' elif sense == '>=': sense = 'G' return sense def set_objective(self, lst): """ :type lst: list[int or (int, float) or (int, int, float)] or float """ if isinstance(lst, float): self._model.objective.set_offset(lst) return linear = [] quad = [] assert isinstance(lst, list) for e in lst: assert isinstance(e, int) or isinstance(e, tuple) if isinstance(e, int): if e > 0: linear.append((e, 1)) elif e < 0: linear.append((-e, -1)) else: raise RuntimeError('invalid variable ID') elif len(e) == 2: linear.append(e) else: assert len(e) == 3 e = (min(e[0], e[1]), max(e[0], e[1]), e[2]) quad.append(e) if linear: self._model.objective.set_linear(linear) if quad: self._model.objective.set_quadratic_coefficients(quad) @staticmethod def _convert_coefficients(coef): """ Convert 'x', and '-x' into ('x', 1) and ('x', -1), respectively. :type coef: list[(int, float) or int] :rtype: (list[int], list[float]) """ ind = [] val = [] for e in coef: if isinstance(e, tuple): assert len(e) == 2 ind.append(e[0]) val.append(e[1]) elif isinstance(e, int): if e >= 0: ind.append(e) val.append(1) else: ind.append(-e) val.append(-1) else: raise RuntimeError('unsupported type:' + str(e)) return ind, val def add_linear_constraint(self, coef, sense, rhs): """ :type coef: list[(int, float)] :type sense: string :type rhs: float :rtype: None """ if not coef: logger.warning('empty linear constraint') return ind, val = self._convert_coefficients(coef) sense = self._convert_sense(sense) c = self._lin c['lin_expr'].append(SparsePair(ind, val)) c['senses'].append(sense) c['rhs'].append(rhs) c['range_values'].append(0) c['names'].append('c' + str(len(self._lin['names']))) # logger.debug('%s %s %s %s', c['names'][-1], c['lin_expr'][-1], c['senses'][-1], c['rhs'][-1]) def add_indicator_constraint(self, indvar, complemented, coef, sense, rhs): """ :type indvar: int :type complemented: int :type coef: list[(int, float)] :type sense: string :type rhs: float :rtype: None """ ind, val = self._convert_coefficients(coef) sense = self._convert_sense(sense) c = { 'lin_expr': SparsePair(ind, val), 'sense': sense, 'rhs': rhs, 'name': 'i' + str(self._model.indicator_constraints.get_num()), 'indvar': indvar, 'complemented': complemented } self._model.indicator_constraints.add(**c) def add_sos(self, coef): """ :type coef: list[(int, float)] """ ind, val = self._convert_coefficients(coef) c = { 'type': '1', 'SOS': SparsePair(ind, val), 'name': 'sos' + str(self._model.SOS.get_num()), } self._model.SOS.add(**c) def add_quadratic_constraint(self, lin, quad, sense, rhs): """ :type lin: list[(int, float)] :type quad: list[(int, int, float)] :type sense: string :type rhs: float :rtype: None """ ind, val = self._convert_coefficients(lin) ind1 = [e[0] for e in quad] ind2 = [e[1] for e in quad] val2 = [e[2] for e in quad] sense = self._convert_sense(sense) c = { 'lin_expr': SparsePair(ind, val), 'quad_expr': SparseTriple(ind1, ind2, val2), 'sense': sense, 'rhs': rhs, 'name': 'q' + str(self._model.quadratic_constraints.get_num()) } self._model.quadratic_constraints.add(**c) def build_model(self): self._model.linear_constraints.add(**self._lin) self._init_lin() def write(self, filename, filetype=''): self._model.write(filename, filetype)
def create_risk_slim(coef_set, input): """ create RiskSLIM MIP object Parameters ---------- input - dictionary of RiskSLIM parameters and formulation Returns ------- mip - RiskSLIM surrogate MIP without 0 cuts Issues ---- no support for non-integer Lset "values" only drops intercept index for variable_names that match '(Intercept)' """ assert isinstance(coef_set, CoefficientSet) assert isinstance(input, dict) # setup printing and loading function_print_flag = input.get('print_flag', False) print_from_function = lambda msg: print_log(msg) if function_print_flag else lambda msg: None update_parameter = lambda pname, pvalue: get_or_set_default(input, pname, pvalue, print_flag = function_print_flag) # set default parameters input = update_parameter('w_pos', 1.0) input = update_parameter('w_neg', 2.0 - input['w_pos']) input = update_parameter('C_0', 0.01) input = update_parameter('include_auxillary_variable_for_objval', True) input = update_parameter('include_auxillary_variable_for_L0_norm', True) input = update_parameter('loss_min', 0.00) input = update_parameter('loss_max', float(CPX_INFINITY)) input = update_parameter('L0_min', 0) input = update_parameter('L0_max', len(coef_set)) input = update_parameter('objval_min', 0.00) input = update_parameter('objval_max', float(CPX_INFINITY)) input = update_parameter('relax_integer_variables', False) input = update_parameter('drop_variables', True) input = update_parameter('tight_formulation', False) input = update_parameter('set_cplex_cutoffs', True) # variables P = len(coef_set) w_pos, w_neg = input['w_pos'], input['w_neg'] C_0j = np.copy(coef_set.c0) L0_reg_ind = np.isnan(C_0j) C_0j[L0_reg_ind] = input['C_0'] C_0j = C_0j.tolist() C_0_rho = np.copy(C_0j) trivial_L0_min = 0 trivial_L0_max = np.sum(L0_reg_ind) rho_ub = list(coef_set.ub) rho_lb = list(coef_set.lb) rho_type = ''.join(list(coef_set.vtype)) # calculate min/max values for loss loss_min = max(0.0, float(input['loss_min'])) loss_max = min(CPX_INFINITY, float(input['loss_max'])) # calculate min/max values for model size L0_min = max(input['L0_min'], 0.0) L0_max = min(input['L0_max'], trivial_L0_max) L0_min = ceil(L0_min) L0_max = floor(L0_max) assert L0_min <= L0_max # calculate min/max values for objval objval_min = max(input['objval_min'], 0.0) objval_max = min(input['objval_max'], CPX_INFINITY) assert objval_min <= objval_max # include constraint on min/max model size? nontrivial_L0_min = L0_min > trivial_L0_min nontrivial_L0_max = L0_max < trivial_L0_max include_auxillary_variable_for_L0_norm = input['include_auxillary_variable_for_L0_norm'] or \ nontrivial_L0_min or \ nontrivial_L0_max # include constraint on min/max objective value? nontrivial_objval_min = objval_min > 0.0 nontrivial_objval_max = objval_max < CPX_INFINITY include_auxillary_variable_for_objval = input['include_auxillary_variable_for_objval'] or \ nontrivial_objval_min or \ nontrivial_objval_max has_intercept = '(Intercept)' in coef_set.variable_names """ RiskSLIM MIP Formulation minimize w_pos*loss_pos + w_neg *loss_minus + 0*rho_j + C_0j*alpha_j such that L0_min <= L0 <= L0_max -rho_min * alpha_j < lambda_j < rho_max * alpha_j L_0 in 0 to P rho_j in [rho_min_j, rho_max_j] alpha_j in {0,1} x = [loss_pos, loss_neg, rho_j, alpha_j] optional constraints: objval = w_pos * loss_pos + w_neg * loss_min + sum(C_0j * alpha_j) (required for callback) L0_norm = sum(alpha_j) (required for callback) Changes for Tight Formulation (included when input['tight_formulation'] = True): sigma_j in {0,1} for j s.t. lambda_j has free sign and alpha_j exists lambda_j >= delta_pos_j if alpha_j = 1 and sigma_j = 1 lambda_j <= -delta_neg_j if alpha_j = 1 and sigma_j = 0 lambda_j >= alpha_j for j such that lambda_j >= 0 lambda_j <= -alpha_j for j such that lambda_j <= 0 """ # create MIP object mip = Cplex() vars = mip.variables cons = mip.linear_constraints # set sense mip.objective.set_sense(mip.objective.sense.minimize) # add main variables loss_obj = [w_pos] loss_ub = [loss_max] loss_lb = [loss_min] loss_type = 'C' loss_names = ['loss'] obj = loss_obj + [0.0] * P + C_0j ub = loss_ub + rho_ub + [1.0] * P lb = loss_lb + rho_lb + [0.0] * P ctype = loss_type + rho_type + 'B' * P rho_names = ['rho_%d' % j for j in range(P)] alpha_names = ['alpha_%d' % j for j in range(P)] varnames = loss_names + rho_names + alpha_names if include_auxillary_variable_for_objval: objval_auxillary_name = ['objval'] objval_auxillary_ub = [objval_max] objval_auxillary_lb = [objval_min] objval_type = 'C' print_from_function("adding auxiliary variable for objval s.t. %1.4f <= objval <= %1.4f" % (objval_min, objval_max)) obj += [0.0] ub += objval_auxillary_ub lb += objval_auxillary_lb varnames += objval_auxillary_name ctype += objval_type if include_auxillary_variable_for_L0_norm: L0_norm_auxillary_name = ['L0_norm'] L0_norm_auxillary_ub = [L0_max] L0_norm_auxillary_lb = [L0_min] L0_norm_type = 'I' print_from_function("adding auxiliary variable for L0_norm s.t. %d <= L0_norm <= %d" % (L0_min, L0_max)) obj += [0.0] ub += L0_norm_auxillary_ub lb += L0_norm_auxillary_lb varnames += L0_norm_auxillary_name ctype += L0_norm_type if input['relax_integer_variables']: ctype = ctype.replace('I', 'C') ctype = ctype.replace('B', 'C') vars.add(obj = obj, lb = lb, ub = ub, types = ctype, names = varnames) # 0-Norm LB Constraints: # lambda_j,lb * alpha_j <= lambda_j <= Inf # 0 <= lambda_j - lambda_j,lb * alpha_j < Inf for j in range(P): cons.add(names = ["L0_norm_lb_" + str(j)], lin_expr = [SparsePair(ind=[rho_names[j], alpha_names[j]], val=[1.0, -rho_lb[j]])], senses = "G", rhs = [0.0]) # 0-Norm UB Constraints: # lambda_j <= lambda_j,ub * alpha_j # 0 <= -lambda_j + lambda_j,ub * alpha_j for j in range(P): cons.add(names = ["L0_norm_ub_" + str(j)], lin_expr =[SparsePair(ind=[rho_names[j], alpha_names[j]], val=[-1.0, rho_ub[j]])], senses = "G", rhs = [0.0]) # objval_max constraint # loss_var + sum(C_0j .* alpha_j) <= objval_max if include_auxillary_variable_for_objval: print_from_function("adding constraint so that objective value <= " + str(objval_max)) cons.add(names = ["objval_def"], lin_expr = [SparsePair(ind = objval_auxillary_name + loss_names + alpha_names, val=[-1.0] + loss_obj + C_0j)], senses = "E", rhs = [0.0]) # Auxiliary L0_norm variable definition: # L0_norm = sum(alpha_j) # L0_norm - sum(alpha_j) = 0 if include_auxillary_variable_for_L0_norm: cons.add(names = ["L0_norm_def"], lin_expr = [SparsePair(ind = L0_norm_auxillary_name + alpha_names, val = [1.0] + [-1.0] * P)], senses = "E", rhs = [0.0]) # drop L0_norm_lb constraint for any variable with rho_lb >= 0 dropped_variables = [] constraints_to_drop = [] # drop alpha / L0_norm_ub / L0_norm_lb for ('Intercept') if input['drop_variables']: # drop L0_norm_ub/lb constraint for any variable with rho_ub/rho_lb >= 0 sign_pos_ind = np.flatnonzero(coef_set.sign > 0) sign_neg_ind = np.flatnonzero(coef_set.sign < 0) constraints_to_drop.extend(["L0_norm_lb_" + str(j) for j in sign_pos_ind]) constraints_to_drop.extend(["L0_norm_ub_" + str(j) for j in sign_neg_ind]) # drop alpha for any variable where rho_ub = rho_lb = 0 fixed_value_ind = np.flatnonzero(coef_set.ub == coef_set.lb) variables_to_drop = ["alpha_" + str(j) for j in fixed_value_ind] vars.delete(variables_to_drop) dropped_variables += variables_to_drop alpha_names = [alpha_names[j] for j in range(P) if alpha_names[j] not in dropped_variables] if has_intercept: intercept_idx = coef_set.variable_names.index('(Intercept)') intercept_alpha_name = 'alpha_' + str(intercept_idx) vars.delete([intercept_alpha_name]) alpha_names.remove(intercept_alpha_name) dropped_variables.append(intercept_alpha_name) print_from_function("dropped L0 indicator for '(Intercept)'") constraints_to_drop.extend(["L0_norm_ub_" + str(intercept_idx), "L0_norm_lb_" + str(intercept_idx)]) if len(constraints_to_drop) > 0: constraints_to_drop = list(set(constraints_to_drop)) cons.delete(constraints_to_drop) # indices indices = { 'n_variables': vars.get_num(), 'n_constraints': cons.get_num(), 'names': vars.get_names(), 'loss_names': loss_names, 'rho_names': rho_names, 'alpha_names': alpha_names, 'loss': vars.get_indices(loss_names), 'rho': vars.get_indices(rho_names), 'alpha': vars.get_indices(alpha_names), 'L0_reg_ind': L0_reg_ind, 'C_0_rho': C_0_rho, 'C_0_alpha': mip.objective.get_linear(alpha_names) if len(alpha_names) > 0 else [], } if include_auxillary_variable_for_objval: indices.update({ 'objval_name': objval_auxillary_name, 'objval': vars.get_indices(objval_auxillary_name)[0], }) if include_auxillary_variable_for_L0_norm: indices.update({ 'L0_norm_name': L0_norm_auxillary_name, 'L0_norm': vars.get_indices(L0_norm_auxillary_name)[0], }) # officially change the problem to LP if variables are relaxed if input['relax_integer_variables']: old_problem_type = mip.problem_type[mip.get_problem_type()] mip.set_problem_type(mip.problem_type.LP) new_problem_type = mip.problem_type[mip.get_problem_type()] print_from_function("changed problem type from %s to %s" % (old_problem_type, new_problem_type)) if input['set_cplex_cutoffs'] and not input['relax_integer_variables']: mip.parameters.mip.tolerances.lowercutoff.set(objval_min) mip.parameters.mip.tolerances.uppercutoff.set(objval_max) return mip, indices
def Quadratic_constraint(self): """Adds Quadratic constraint to the model's Gurobi/Cplex Interface. (x-mu).T @ inv(cov) @ (x-mu) <= chi-square Note: This one creates one ellipsoidal constraint for all the metabolites that has non zero or non 'nan' formation energy, irrespective of the magnitude of variance. if the model is infeasible after adding this constraint, refer to util_func.py, find_correlated metabolites to add different ellipsoidal constraints to high variance and normal compounds to avoid possible numerical issues. Unable to retrieve quadratic constraints in Gurobi model, can see the QC when printed. :raises NotImplementedError: Implemented only for Gurobi/Cplex interfaces. :return: [description] :rtype: [type] """ # Pick indices of components present in the current model model_component_indices = [ i for i in range(self.compound_vector_matrix.shape[1]) if np.any(self.compound_vector_matrix[:, i]) ] # Reduced the compound_vector to contain only the non zero entries model_compound_vector = self.compound_vector_matrix[:, model_component_indices] # Now extract the sub covariance matrix containing only the components present in the model component_model_covariance = covariance[:, model_component_indices][ model_component_indices, :] # Now separate the compounds that have variance > 1000 and others to avoid numerical issues high_variance_indices = np.where( np.diag(component_model_covariance) > 1000)[0] low_variance_indices = np.where( np.diag(component_model_covariance) < 1000)[0] # Calculate cholesky matrix for two different covariance matrices if len(low_variance_indices) > 0: small_component_covariance = component_model_covariance[:, low_variance_indices][ low_variance_indices, :] cholesky_small_variance = matrix_decomposition( small_component_covariance) chi2_value_small = stats.chi2.isf( q=0.05, df=cholesky_small_variance.shape[1] ) # Chi-square value to map confidence interval for i in high_variance_indices: zeros_axis = np.zeros((cholesky_small_variance.shape[1], )) cholesky_small_variance = np.insert(cholesky_small_variance, i, zeros_axis, axis=0) metabolite_sphere_small = ( model_compound_vector @ cholesky_small_variance ) # This is a fixed term compound_vector @ cholesky if len(high_variance_indices) > 0: large_component_covariance = component_model_covariance[:, high_variance_indices][ high_variance_indices, :] # Covariance matrix for the high variance components cholesky_large_variance = matrix_decomposition( large_component_covariance) chi2_value_high = stats.chi2.isf( q=0.05, df=cholesky_large_variance.shape[1]) # Insert empty rows for the low_variance_components for i in low_variance_indices: zeros_axis = np.zeros((cholesky_large_variance.shape[1], )) cholesky_large_variance = np.insert(cholesky_large_variance, i, zeros_axis, axis=0) metabolite_sphere_large = ( model_compound_vector @ cholesky_large_variance ) # This is a fixed term compound_vector @ cholesky proton_indices = [ self.metabolites.index(metabolite) for metabolite in self.metabolites if metabolite.equilibrator_accession is not None if metabolite.equilibrator_accession.inchi_key == PROTON_INCHI_KEY ] # Get indices of protons in metabolite list to avoid double correcting them for concentrations if self.solver.__class__.__module__ == "optlang.cplex_interface": from cplex import Cplex, SparsePair, SparseTriple # Instantiate Cplex model cplex_model = Cplex() rand_str = "".join( choices(string.ascii_lowercase + string.digits, k=6)) # write cplex model to mps file in random directory and re read with tempfile.TemporaryDirectory() as td: temp_filename = os.path.join(td, rand_str + ".mps") self.solver.problem.write(temp_filename) cplex_model.read(temp_filename) # Stop printing output in cplex cplex_model.set_log_stream(None) cplex_model.set_error_stream(None) cplex_model.set_warning_stream(None) cplex_model.set_results_stream(None) # Remove the unnecessary variables and constraints remove_vars = [ var for var in cplex_model.variables.get_names() if var.startswith("component_") or var.startswith("dG_err_") ] # Remove error variables remove_constrs = [ cons for cons in cplex_model.linear_constraints.get_names() if cons.startswith("delG_") or cons.startswith("std_dev_") ] # Remove delG constraint and re-add with component variables cplex_model.linear_constraints.delete( remove_constrs) # Removing constr cplex_model.variables.delete(remove_vars) # Removing Vars # QC for small variance components if len(low_variance_indices) > 0: indices_sphere1 = cplex_model.variables.add( names=[ "Sphere1_{}".format(i) for i in range(cholesky_small_variance.shape[1]) ], lb=[-1] * cholesky_small_variance.shape[1], ub=[1] * cholesky_small_variance.shape[1], ) # Adding independent component variables to the model, store the variable indices # Add the Sphere constraint cplex_model.quadratic_constraints.add( quad_expr=SparseTriple( ind1=indices_sphere1, ind2=indices_sphere1, val=len(indices_sphere1) * [1], ), sense="L", rhs=1, name="unit_normal_small_variance", ) else: indices_sphere1 = [ ] # Just to adjust the matrix dimensions later # QC for large variance components if len(high_variance_indices) > 0: indices_sphere2 = cplex_model.variables.add( names=[ "Sphere2_{}".format(i) for i in range(cholesky_large_variance.shape[1]) ], lb=[-1] * cholesky_large_variance.shape[1], ub=[1] * cholesky_large_variance.shape[1], ) # Independent large variance components cplex_model.quadratic_constraints.add( quad_expr=SparseTriple( ind1=indices_sphere2, ind2=indices_sphere2, val=len(indices_sphere2) * [1], ), rhs=1, sense="L", name="unit_normal_high_variance", ) else: indices_sphere2 = [] # Balancing matrix dimensions concentration_variables = [ "lnc_{}".format(metabolite.id) for metabolite in self.metabolites ] # Add the delG constraints for reaction in self.reactions: if reaction.id in self.Exclude_reactions: continue rxn_stoichiometry = reaction.cal_stoichiometric_matrix() rxn_stoichiometry = rxn_stoichiometry[np.newaxis, :] if len(low_variance_indices) > 0: coefficient_matrix_small_variance = ( np.sqrt(chi2_value_small) * rxn_stoichiometry @ metabolite_sphere_small ) # Coefficient array for small variance ellipsoid else: coefficient_matrix_small_variance = np.array(()) if len(high_variance_indices) > 0: coefficient_matrix_large_variance = ( np.sqrt(chi2_value_high) * rxn_stoichiometry @ metabolite_sphere_large ) # Coefficient array for large variance ellipsoid else: coefficient_matrix_large_variance = np.array(()) concentration_coefficients = RT * rxn_stoichiometry concentration_coefficients[0, proton_indices] = 0 coefficients_forward = np.hstack(( np.array((1)), -1 * concentration_coefficients.flatten(), -1 * coefficient_matrix_small_variance.flatten(), -1 * coefficient_matrix_large_variance.flatten(), )) coefficients_reverse = np.hstack(( np.array((1)), concentration_coefficients.flatten(), coefficient_matrix_small_variance.flatten(), coefficient_matrix_large_variance.flatten(), )) variable_order_forward = ( ["dG_{}".format(reaction.forward_variable.name)] + concentration_variables + list(indices_sphere1) + list(indices_sphere2)) variable_order_reverse = ( ["dG_{}".format(reaction.reverse_variable.name)] + concentration_variables + list(indices_sphere1) + list(indices_sphere2)) rhs = reaction.delG_prime + reaction.delG_transport cplex_model.linear_constraints.add( lin_expr=[ SparsePair( ind=variable_order_forward, val=coefficients_forward.tolist(), ) ], senses=["E"], rhs=[rhs], names=["delG_{}".format(reaction.forward_variable.name)], ) # delG constraint for forward reaction cplex_model.linear_constraints.add( lin_expr=[ SparsePair( ind=variable_order_reverse, val=coefficients_reverse.tolist(), ) ], senses=["E"], rhs=[-rhs], names=["delG_{}".format(reaction.reverse_variable.name)], ) # delG constraint for reverse reaction return cplex_model elif self.solver.__class__.__module__ == "optlang.gurobi_interface": from gurobipy import GRB, LinExpr gurobi_model = self.solver.problem.copy() # Remove unnecessary variables and constraints and rebuild appropriate ones remove_vars = [ var for var in gurobi_model.getVars() if var.VarName.startswith("component_") or var.VarName.startswith("dG_err_") ] remove_constrs = [ cons for cons in gurobi_model.getConstrs() if cons.ConstrName.startswith("delG_") or cons.ConstrName.startswith("std_dev_") ] gurobi_model.remove(remove_constrs + remove_vars) # Add sphere variables for smaller set and larger set separately if len(low_variance_indices) > 0: for i in range(cholesky_small_variance.shape[1]): gurobi_model.addVar(lb=-1, ub=1, name="Sphere1_{}".format(i)) gurobi_model.update() sphere1_variables = [ var for var in gurobi_model.getVars() if var.VarName.startswith("Sphere1_") ] gurobi_model.addQConstr( np.sum(np.square(np.array(sphere1_variables))) <= 1, name="unit_normal_small_variance", ) gurobi_model.update() else: sphere1_variables = [] # QC for large variance components if len(high_variance_indices) > 0: for i in range(cholesky_large_variance.shape[1]): gurobi_model.addVar(lb=-1, ub=1, name="Sphere2_{}".format(i)) gurobi_model.update() sphere2_variables = [ var for var in gurobi_model.getVars() if var.VarName.startswith("Sphere2_") ] gurobi_model.addQConstr( np.sum(np.square(np.array(sphere2_variables))) <= 1, name="unit_normal_high_variance", ) gurobi_model.update() else: sphere2_variables = [] # Create a list of metabolite concentration variables concentration_variables = [] for metabolite in self.metabolites: varname = "lnc_{}".format(metabolite.id) conc_var = gurobi_model.getVarByName(varname) concentration_variables.append(conc_var) # Add the delG constraints for reaction in self.reactions: if reaction.id in self.Exclude_reactions: continue rxn_stoichiometry = reaction.cal_stoichiometric_matrix() rxn_stoichiometry = rxn_stoichiometry[np.newaxis, :] if len(low_variance_indices) > 0: coefficient_matrix_small_variance = ( np.sqrt(chi2_value_small) * rxn_stoichiometry @ metabolite_sphere_small ) # Coefficient array for small variance ellipsoid else: coefficient_matrix_small_variance = np.array(()) if len(high_variance_indices) > 0: coefficient_matrix_large_variance = ( np.sqrt(chi2_value_high) * rxn_stoichiometry @ metabolite_sphere_large ) # Coefficient array for large variance ellipsoid else: coefficient_matrix_large_variance = np.array(()) concentration_coefficients = RT * rxn_stoichiometry concentration_coefficients[0, proton_indices] = 0 coefficients_forward = np.hstack(( -1 * concentration_coefficients.flatten(), -1 * coefficient_matrix_small_variance.flatten(), -1 * coefficient_matrix_large_variance.flatten(), )) coefficients_reverse = np.hstack(( concentration_coefficients.flatten(), coefficient_matrix_small_variance.flatten(), coefficient_matrix_large_variance.flatten(), )) variable_order = (concentration_variables + sphere1_variables + sphere2_variables) delG_err_forward = LinExpr(coefficients_forward.tolist(), variable_order) delG_err_reverse = LinExpr(coefficients_reverse.tolist(), variable_order) delG_for_var = gurobi_model.getVarByName("dG_{}".format( reaction.forward_variable.name)) delG_rev_var = gurobi_model.getVarByName("dG_{}".format( reaction.reverse_variable.name)) rhs = reaction.delG_prime + reaction.delG_transport gurobi_model.addConstr( delG_for_var + delG_err_forward, GRB.EQUAL, rhs, name="delG_{}".format(reaction.forward_variable.name), ) gurobi_model.addConstr( delG_rev_var + delG_err_reverse, GRB.EQUAL, -rhs, name="delG_{}".format(reaction.reverse_variable.name), ) gurobi_model.update() return gurobi_model else: raise NotImplementedError("Current solver doesn't support QC") logging.error( "Current solver doesnt support problesm of type MIQC")
def __init__(self): Cplex.__init__(self)
def create_problem(cobra_model, quadratic_component=None, **kwargs): """Solver-specific method for constructing a solver problem from a cobra.Model. This can be tuned for performance using kwargs """ # Process parameter defaults the_parameters = parameter_defaults if kwargs: the_parameters = parameter_defaults.copy() the_parameters.update(kwargs) if 'relax_b' in the_parameters: relax_b = the_parameters.pop("relax_b") warn('need to reimplement relax_b') relax_b = False else: relax_b = False # Begin problem creation lp = Cplex() for k, v in iteritems(the_parameters): set_parameter(lp, k, v) objective_coefficients = [ float(x.objective_coefficient) for x in cobra_model.reactions ] lower_bounds = [_float(x.lower_bound) for x in cobra_model.reactions] upper_bounds = [_float(x.upper_bound) for x in cobra_model.reactions] variable_names = cobra_model.reactions.list_attr("id") variable_kinds = [ variable_kind_dict[x.variable_kind] for x in cobra_model.reactions ] # Cplex decides that the problem is a MIP if variable_kinds are supplied # even if there aren't any integers. if variable_kind_dict['integer'] in variable_kinds: lp.variables.add(obj=objective_coefficients, lb=lower_bounds, ub=upper_bounds, names=variable_names, types=variable_kinds) else: lp.variables.add(obj=objective_coefficients, lb=lower_bounds, ub=upper_bounds, names=variable_names) constraint_sense = [] constraint_names = [] constraint_limits = [] for x in cobra_model.metabolites: constraint_sense.append(x._constraint_sense) constraint_names.append(x.id) constraint_limits.append(float(x._bound)) the_linear_expressions = [] # NOTE: This won't work with metabolites that aren't in any reaction for the_metabolite in cobra_model.metabolites: variable_list = [] coefficient_list = [] for the_reaction in the_metabolite._reaction: variable_list.append(the_reaction.id) coefficient_list.append( _float(the_reaction._metabolites[the_metabolite])) the_linear_expressions.append( SparsePair(ind=variable_list, val=coefficient_list)) # Set objective to quadratic program if quadratic_component is not None: set_quadratic_objective(lp, quadratic_component) if relax_b: lp.linear_constraints.add(lin_expr=the_linear_expressions, rhs=constraint_limits, range_values=list(range_values), senses=constraint_sense, names=constraint_names) else: lp.linear_constraints.add(lin_expr=the_linear_expressions, rhs=constraint_limits, senses=constraint_sense, names=constraint_names) # Set the problem type as cplex doesn't appear to do this correctly problem_type = Cplex.problem_type.LP if Cplex.variables.type.integer in variable_kinds: if quadratic_component is not None: problem_type = Cplex.problem_type.MIQP else: problem_type = Cplex.problem_type.MILP elif quadratic_component is not None: problem_type = Cplex.problem_type.QP lp.set_problem_type(problem_type) return (lp)
class CCOPF: def __init__(self, conf='conf9', data_dir='data', solver='cplex', verbose=True): if verbose: print 'loading problem data' self.conf = conf self.solver = solver self.tol = 1e-5 self.verbose = verbose bus, branch, Bbus, Binv, self.line_nu, self.gen_nu = loadfiles.load( self.conf, data_dir) self.data = [bus, branch, Bbus, Binv] def set_aux(self): ''' Helper variables built from the input data. Call this after changing the data; don't modify these directly ''' bus, branch, Bbus, Binv = self.data n = Bbus.shape[0] # number of buses nl = len(branch) # number of lines d = [bus[b]['d'] for b in bus] # demand mu = [bus[b].get('avg', 0) for b in bus] # mean wind power winds = {b for b in bus if 'std' in bus[b]} # wind source bus IDs sigma = {b: bus[b]['std'] for b in bus if b in winds} # std wind power gens = {b for b in bus if bus[b]['type'] == 2} # generator bus IDs self.aux = [n, nl, d, mu, winds, sigma, gens] def formulate(self): self.set_aux() self.M = Cplex() if self.solver == 'cplex' else Model() self.addVars() self.addCons() self.setObj() self.p, self.a, self.obj, self.converged, self.tot_its = None,None,None,False,0 if self.verbose: print 'solving with', self.solver print '%2s %10s %8s %4s %3s %10s' % ('it', 'worst line', 'viol', 'stat', 'its', 'obj') def addVars(self): bus,branch,_,_, n,nl,_,_,_,_,gens = self.data + self.aux if self.verbose: print 'defining variables' INF = 1e100 if self.solver == 'cplex': p = ['p_%d'%i for i in gens] a = ['a_%d'%i for i in gens] D = ['D_%d'%i for i in bus] t = ['t_%d'%i for i in bus] m = ['m{}'.format(i['id']) for i in branch] s = ['s{}'.format(i['id']) for i in branch] self.M.variables.add(names = p + a) self.M.variables.add(names = D + t, lb = [-INF]*2*n) #self.M.variables.add(names = m, lb = [-INF]*nl) #self.M.variables.add(names = s) self.M.variables.add(names = m + s, lb = [-INF]*2*nl) D, t = arr(D), arr(t) self.var = (p, a, D, t, m, s) else: p = {i: self.M.addVar(name='pbar_%d'%i) for i in gens} a = {i: self.M.addVar(name='alpha_%d'%i) for i in gens} D = {i: self.M.addVar(lb=-INF, name='delta_%d'%i) for i in bus} t = {i: self.M.addVar(lb=-INF, name='theta_%d'%i) for i in bus} m = {i['id']: self.M.addVar(lb=-INF, name='fbar{}'.format(i['id'])) for i in branch} s = {i['id']: self.M.addVar(lb=-INF, name='std{}'.format(i['id'])) for i in branch} self.var = (p, a, D, t, m, s) self.M.update() def addCons(self): bus,branch,Bbus,_, line_nu,gen_nu, n,_,d,mu,winds,sigma,gens = self.data + \ [self.line_nu, self.gen_nu] + self.aux if self.verbose: print 'defining constraints' p, a, D, t, m, s = self.var sumvar = sum(sigma[b]**2 for b in winds) if self.solver == 'cplex': ng = len(gens) self.M.linear_constraints.add(lin_expr = [sp(ind = a, val = [1.0]*ng)], rhs = [1.0], names=['sum_alpha']) self.M.linear_constraints.add(lin_expr = [sp(ind = p, val = [1.0]*ng)], rhs = [sum(d)-sum(mu)], names=['power_balance']) for i in xrange(n-1): Bi = Bbus[i,:-1].tocoo() J,V = Bi.col, Bi.data if i in gens: pi, ai, Di, ti = 'p_%d'%i, 'a_%d'%i, 'delta_%d'%i, 'theta_%d'%i self.M.linear_constraints.add(lin_expr = [sp(ind = list(D[J])+[ai], val = list(V)+[-1]), sp(ind = list(t[J])+[pi], val = list(V)+[-1])], rhs=[0, mu[i]-d[i]], names=[Di, ti], senses='EE') self.M.linear_constraints.add(lin_expr = [sp(ind = [pi,ai], val = [1, sumvar**.5*gen_nu]), sp(ind = [pi,ai], val = [1, -sumvar**.5*gen_nu])], rhs=[bus[i]['pmax'], bus[i]['pmin']], names=['gen+_%d'%i, 'gen-_%d'%i], senses='LG') else: self.M.linear_constraints.add(lin_expr = [sp(ind = D[J], val = V), sp(ind = t[J], val = V)], rhs=[0, mu[i]-d[i]], names=['delta_%d'%i, 'theta_%d'%i], senses='EE') self.M.linear_constraints.add(lin_expr = [sp(ind = [D[n-1]], val = [1.0]), sp(ind = [t[n-1]], val = [1.0])], rhs = [0,0], names=['delta_%d'%(n-1), 'theta_%d'%(n-1)], senses='EE') for line in branch: ID = line['id'] ij = line['ij'] i,j = ij mij, sij = 'm{}'.format(ID), 's{}'.format(ID) self.M.linear_constraints.add(lin_expr = [sp(ind = [mij, t[i], t[j]], val = [1.0, -line['y'], line['y']])], rhs = [0], names=['line_avg_{}'.format(ID)], senses='E') self.M.linear_constraints.add(lin_expr = [sp(ind = [mij, sij], val = [-1.0, line_nu]), sp(ind = [mij, sij], val = [1.0, line_nu])], names=['line+_{}'.format(ID), 'line-_{}'.format(ID)], rhs = [line['fmax'], line['fmax']], senses='LL') else: # gurobi is much nicer self.M.addConstr(qs(a[i] for i in gens) == 1, 'sum_alpha_1') self.M.addConstr(qs(p[i] for i in gens) + sum(mu) == sum(d), 'power_balance') for i in xrange(n-1): Bi = Bbus[i,:-1].tocoo() Bi = zip(Bi.col, Bi.data) if i in gens: self.M.addConstr(qs(v*D[j] for j,v in Bi) == a[i], 'delta_%d'%i) self.M.addConstr(qs(v*t[j] for j,v in Bi) == p[i]+mu[i]-d[i], 'theta_%d'%i) self.M.addConstr(sumvar**.5*a[i]*gen_nu <= bus[i]['pmax']-p[i], 'gen+_%d'%i) self.M.addConstr(sumvar**.5*a[i]*gen_nu <= -bus[i]['pmin']+p[i], 'gen-_%d'%i) else: self.M.addConstr(qs(v*D[j] for j,v in Bi) == 0, 'delta_%d'%i) self.M.addConstr(qs(v*t[j] for j,v in Bi) == mu[i]-d[i], 'theta_%d'%i) self.M.addConstr(D[n-1] == 0, 'delta') self.M.addConstr(t[n-1] == 0, 'theta') for line in branch: ID = line['id'] ij = line['ij'] i,j = ij self.M.addConstr(m[ID] == line['y']*(t[i]-t[j]), 'line_avg_{}'.format(ID)) self.M.addConstr(s[ID]*line_nu <= line['fmax'] - m[ID], 'line+_{}'.format(ID)) self.M.addConstr(s[ID]*line_nu <= line['fmax'] + m[ID], 'line-_{}'.format(ID)) def setObj(self): bus,_,_,_, _,_,_,_,winds,sigma,gens = self.data + self.aux p, a, D, t, m, s = self.var cost = {b: bus[b]['cost'] for b in bus if b in gens} sumvar = sum(sigma[b]**2 for b in winds) if self.solver == 'cplex': ng = len(gens) nv = self.M.variables.get_num() cost = [2*cost[i] for i in gens] + [2*sumvar*cost[i] for i in gens] + \ [0.0]*(nv-2*ng) self.M.objective.set_quadratic(cost) self.M.set_results_stream(None) else: self.M.setObjective(qs(cost[i]*(p[i]*p[i] + sumvar*a[i]*a[i]) for i in gens)) def solve_subproblem(self): if self.solver == 'cplex': self.M.solve() sol = self.M.solution self.obj = sol.get_objective_value() stat = sol.get_status() if stat == sol.status.optimal: self.status = 'good' elif stat == sol.status.num_best: self.status = 'okay' else: self.status = 'fail' self.statcode = stat self.its = sol.progress.get_num_barrier_iterations() self.sol = sol else: self.M.params.outputFlag = 0 self.M.optimize() self.obj = None if self.M.status == GRB.OPTIMAL: self.status = 'good' self.obj = self.M.ObjVal elif self.M.status == GRB.SUBOPTIMAL: self.status = 'okay' self.obj = self.M.ObjVal else: self.status = 'fail' self.statcode = self.M.status self.its = self.M.BarIterCount self.tot_its += self.its def compute_max_viol(self, it): tol, _,branch,_,Binv, line_nu, n,_,_,_,winds,sigma,gens = [self.tol] + \ self.data + [self.line_nu] + self.aux p, a, D, t, m, s = self.var self.worst = {'line': -1, 'val': 0} for index, line in enumerate(branch): ID = line['id'] i,j = line['ij'] y, fmax = line['y'], line['fmax'] if self.solver == 'cplex': Di, Dj, mij = self.sol.get_values([D[i], D[j], 'm{}'.format(ID)]) else: Di, Dj, mij = D[i].x, D[j].x, m[ID].x x = arr([y*(Binv[k][i]-Binv[k][j]-Di+Dj)*sigma[k] for k in winds]) violation = (abs(mij)+line_nu*norm(x)-fmax)/fmax if violation > self.worst['val']: self.worst = {'line': index, 'val': violation} if self.worst['val'] < tol: self.converged = True self.p, self.a = zeros(n), zeros(n) for i in gens: if self.solver == 'cplex': pi, ai = 'p_%d'%i, 'a_%d'%i self.p[i], self.a[i] = self.sol.get_values(pi), self.sol.get_values(ai) else: self.p[i], self.a[i] = p[i].x, a[i].x if self.verbose: i = self.worst['line'] print '%2d %4d->%-4d %8.2e %4s %3d %10.4e' % (it+1, branch[i]['ij'][0], branch[i]['ij'][1], self.worst['val'], self.status, self.its, self.obj) def add_cut(self, it): _,branch,_,Binv, _,_,_,_,winds,sigma,_ = self.data + self.aux p, a, D, t, m, s = self.var index = self.worst['line'] ID = branch[index]['id'] i,j = branch[index]['ij'] y = branch[index]['y'] if self.solver == 'cplex': Di, Dj = self.sol.get_values([D[i], D[j]]) sij = 's{}'.format(ID) Bij = {k: Binv[k][i]-Binv[k][j] for k in winds} rhs = y**2*sum([sigma[k]**2*Bij[k]*(Bij[k]-Di+Dj) for k in winds]) coeff = y**2*sum([sigma[k]**2*(Bij[k]-Di+Dj) for k in winds]) x = arr([y*(Bij[k]-Di+Dj)*sigma[k] for k in winds]) #self.M.linear_constraints.add(lin_expr = [sp(ind = [sij], val = [1.0])], #rhs = [0], names=['cut_help%d'%it], senses='G') self.M.linear_constraints.add(lin_expr = [sp(ind = [sij, D[i], D[j]], val = [norm(x), coeff, -coeff])], rhs = [rhs], names=['cut_%d'%it], senses='G') else: x = arr([y*(Binv[k][i]-Binv[k][j]-D[i].x+D[j].x)*sigma[k] for k in winds]) self.M.addConstr(s[ID] >= 0, 'cut_%d_help'%(it+1)) self.M.addConstr(qs(x[l]*y*(Binv[k][i]-Binv[k][j]-D[i]+D[j])*sigma[k] for l,k in enumerate(winds)) <= s[ID]*norm(x), 'cut_%d'%it) def set_mean(self, mu): ''' mu is a dictionary {busID: mean wind} ''' bus = self.data[0] winds = {b for b in bus if 'avg' in bus[b]} if winds != set(mu.keys()): print 'error: mu is different' return None for i, mean in mu.iteritems(): bus[i]['avg'] = mean self.set_aux() def set_std(self, sigma): ''' sigma is a dictionary {busID: std wind} ''' bus = self.data[0] winds = {b for b in bus if 'std' in bus[b]} if winds != set(sigma.keys()): print 'error: sigma is different' return None for i, std in sigma.iteritems(): bus[i]['std'] = std self.set_aux() def scale_load(self, alpha): bus = self.data[0] for i in bus: bus[i]['d'] *= alpha self.set_aux() def scale_lim(self, alpha): branch = self.data[1] for line in branch: line['fmax'] *= alpha self.set_aux()
#!/usr/bin/env python3 import cplex from cplex import Cplex from cplex.exceptions import CplexError import numpy as np import matplotlib.pyplot as plt mip_solver = Cplex() # mip_solver.set_results_stream(None) # mip_solver.set_warning_stream(None) # mip_solver.set_error_stream(None) #mip_solver.parameters.threads.set(1) hidden_weights = np.load("hidden_weights.npy") hidden_bias = np.load("hidden_bias.npy") output_weights = np.load("output_weights.npy") output_bias = np.load("output_bias.npy") input_dim = 28 * 28 hidden_nodes = 20 #mip_solver.objective.set_sense(mip_solver.objective.sense.minimize) mip_solver.objective.set_sense(mip_solver.objective.sense.maximize) # Set the value of the output variable as objective function mip_solver.variables.add(obj=[1], lb=[-cplex.infinity], ub=[cplex.infinity], types="C",
def run_docplex_check_list(): check_platform() from docplex.version import latest_cplex_major, latest_cplex_minor cplex_latest_version_as_tuple = (latest_cplex_major, latest_cplex_minor) diagnostics = [] # check requirements for rm in ["six", "enum", "cloudpickle"]: if not check_import(rm): diagnostics.append( "Module {0} is missing, run: pip install {0}".format(rm)) # check pandas try: import pandas as pd # @UnusedImport # noinspection PyUnresolvedReferences from pandas import DataFrame DataFrame({}) except ImportError: print("-- pandas is not present, some features might be unavailable.") from docplex.mp.environment import Environment Environment().print_information() # check cplex try: # noinspection PyUnresolvedReferences from cplex import Cplex cpx = Cplex() cpxv = cpx.get_version() cpxvt = tuple(float(x) for x in cpx.get_version().split("."))[:2] lcpxv = ".".join(str(z) for z in cplex_latest_version_as_tuple) if cpxvt < cplex_latest_version_as_tuple: print( "Warning: Your cplex version {0} is not the latest, {1} is available" .format(cpxv, lcpxv)) elif cpxvt > cplex_latest_version_as_tuple: print( "* Your cplex version {0} is ahead of the latest DOcplex-compatible version {1}, this might not be compatible." .format(cpxv, lcpxv)) else: print("* Your cplex version {0} is the latest available".format( cpxv)) cpx.end() except ImportError as ie: Cplex = None diagnostics.append("No local installation of CPLEX has been found.") print("Cplex DLL not found, error importing cplex: {0!s}".format(ie)) check_python_path(diagnostics) # check creation of an empty model... try: if Cplex: # noinspection PyUnresolvedReferences from docplex.mp.model import Model Model() # promotional? if Model.is_cplex_ce(): print( "! Cplex promotional version, limited to 1000 variables, 1000 constraints" ) diagnostics.append( "Your local CPLEX edition is limited. Consider purchasing a full license." ) except ImportError: print("Docplex is not present: cannot import class docplex.mp.model") diagnostics.append("Your installation of DOcplex may be corrupted.") except Exception as e: print( "Exception raised when creating one model instance: {0!s}".format( e)) diagnostics.append("Your installation of DOcplex may be corrupted.") if diagnostics: print("\n!! diagnostics: {0}".format(len(diagnostics))) for s in diagnostics: print(" -- {0}".format(s)) else: print("> No problem found: you're all set!")
class CplexSolver(Solver): """ Implements the solver interface using CPLEX. """ def __init__(self, model=None): Solver.__init__(self) self.problem = Cplex() self.status_mapping = { self.problem.solution.status.optimal: Status.OPTIMAL, self.problem.solution.status.optimal_tolerance: Status.OPTIMAL, self.problem.solution.status.unbounded: Status.UNBOUNDED, self.problem.solution.status.infeasible: Status.INFEASIBLE, self.problem.solution.status.infeasible_or_unbounded: Status.INF_OR_UNB, self.problem.solution.status.MIP_optimal: Status.OPTIMAL, self.problem.solution.status.MIP_unbounded: Status.UNBOUNDED, self.problem.solution.status.MIP_infeasible: Status.INFEASIBLE, self.problem.solution.status.MIP_infeasible_or_unbounded: Status.INF_OR_UNB } self.vartype_mapping = { VarType.BINARY: self.problem.variables.type.binary, VarType.INTEGER: self.problem.variables.type.integer, VarType.CONTINUOUS: self.problem.variables.type.continuous } self.parameter_mapping = { Parameter.TIME_LIMIT: self.problem.parameters.timelimit, Parameter.FEASIBILITY_TOL: self.problem.parameters.simplex.tolerances.feasibility, Parameter.OPTIMALITY_TOL: self.problem.parameters.simplex.tolerances.optimality, Parameter.INT_FEASIBILITY_TOL: self.problem.parameters.mip.tolerances.integrality, Parameter.MIP_ABS_GAP: self.problem.parameters.mip.tolerances.mipgap, Parameter.MIP_REL_GAP: self.problem.parameters.mip.tolerances.absmipgap, Parameter.POOL_SIZE: self.problem.parameters.mip.limits.populate, Parameter.POOL_GAP: self.problem.parameters.mip.pool.relgap } self.set_logging() self.set_parameters(default_parameters) # self.problem.parameters.randomseed.set(0) # self.problem.parameters.mip.strategy.search.set(1) self._cached_lin_obj = {} self._cached_sense = None self._cached_lower_bounds = {} self._cached_upper_bounds = {} self._cached_vars = [] self._cached_constrs = [] if model: self.build_problem(model) def add_variable(self, var_id, lb=None, ub=None, vartype=VarType.CONTINUOUS, persistent=True, update_problem=True): """ Add a variable to the current problem. Arguments: var_id (str): variable identifier lb (float): lower bound ub (float): upper bound vartype (VarType): variable type (default: CONTINUOUS) persistent (bool): if the variable should be reused for multiple calls (default: true) update_problem (bool): update problem immediately """ if update_problem: self.add_variables([var_id], [lb], [ub], [vartype]) else: self._cached_vars.append((var_id, lb, ub, vartype)) if not persistent: self.temp_vars.add(var_id) def add_variables(self, var_ids, lbs, ubs, vartypes): """ Add multiple variables to the current problem. Arguments: var_ids (list): variable identifier lbs (list): lower bounds ubs (list): upper bounds vartypes (list): variable types (default: CONTINUOUS) """ lbs = [lb if lb is not None else -infinity for lb in lbs] ubs = [ub if ub is not None else infinity for ub in ubs] if set(vartypes) == {VarType.CONTINUOUS}: self.problem.variables.add(names=var_ids, lb=lbs, ub=ubs) else: vartypes = [self.vartype_mapping[vartype] for vartype in vartypes] self.problem.variables.add(names=var_ids, lb=lbs, ub=ubs, types=vartypes) self.var_ids.extend(var_ids) self._cached_lower_bounds.update(dict(zip(var_ids, lbs))) self._cached_upper_bounds.update(dict(zip(var_ids, ubs))) self._cached_lin_obj.update({var_id: 0.0 for var_id in var_ids}) def add_constraint(self, constr_id, lhs, sense='=', rhs=0, persistent=True, update_problem=True): """ Add a constraint to the current problem. Arguments: constr_id (str): constraint identifier lhs (dict): variables and respective coefficients sense (str): constraint sense (any of: '<', '=', '>'; default '=') rhs (float): right-hand side of equation (default: 0) persistent (bool): if the variable should be reused for multiple calls (default: True) update_problem (bool): update problem immediately """ if update_problem: self.add_constraints([constr_id], [lhs], [sense], [rhs]) else: self._cached_constrs.append((constr_id, lhs, sense, rhs)) if not persistent: self.temp_constrs.add(constr_id) def add_constraints(self, constr_ids, lhs, senses, rhs): """ Add a list of constraints to the current problem. Arguments: constr_ids (list): constraint identifiers lhs (list): variables and respective coefficients senses (list): constraint senses (default: '=') rhs (list): right-hand side of equations (default: 0) """ map_sense = {'=': 'E', '<': 'L', '>': 'G'} exprs = [ SparsePair(ind=list(constr.keys()), val=list(constr.values())) for constr in lhs ] senses = [map_sense[sense] for sense in senses] self.problem.linear_constraints.add(lin_expr=exprs, senses=senses, rhs=rhs, names=constr_ids) self.constr_ids.extend(constr_ids) def remove_variable(self, var_id): """ Remove a variable from the current problem. Arguments: var_id (str): variable identifier """ self.remove_variables([var_id]) def remove_variables(self, var_ids): """ Remove variables from the current problem. Arguments: var_ids (list): variable identifiers """ found = [] for var_id in var_ids: if var_id in self.var_ids: found.append(var_id) self.var_ids.remove(var_id) self.problem.variables.delete(found) def remove_constraint(self, constr_id): """ Remove a constraint from the current problem. Arguments: constr_id (str): constraint identifier """ self.remove_constraints([constr_id]) def remove_constraints(self, constr_ids): """ Remove constraints from the current problem. Arguments: constr_ids (list): constraint identifiers """ found = [] for constr_id in constr_ids: if constr_id in self.constr_ids: found.append(constr_id) self.constr_ids.remove(constr_id) self.problem.linear_constraints.delete(found) def update(self): """ Update internal structure. Used for efficient lazy updating. """ if self._cached_vars: var_ids, lbs, ubs, vartypes = list(zip(*self._cached_vars)) self.add_variables(var_ids, lbs, ubs, vartypes) self._cached_vars = [] if self._cached_constrs: constr_ids, lhs, senses, rhs = list(zip(*self._cached_constrs)) self.add_constraints(constr_ids, lhs, senses, rhs) self._cached_constrs = [] 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: updated_coeffs = {} for var_id in self.var_ids: if var_id in linear and linear[var_id] != self._cached_lin_obj[ var_id]: updated_coeffs[var_id] = linear[var_id] if var_id not in linear and self._cached_lin_obj[var_id] != 0.0: updated_coeffs[var_id] = 0.0 if updated_coeffs: self.problem.objective.set_linear(list(updated_coeffs.items())) self._cached_lin_obj.update(updated_coeffs) if quadratic: self.problem.objective.set_quadratic( [0.0] * len(self.var_ids)) #TODO: is this really necessary ? quad_coeffs = [(r_id1, r_id2, coeff) for (r_id1, r_id2), coeff in quadratic.items()] self.problem.objective.set_quadratic_coefficients(quad_coeffs) if minimize != self._cached_sense: if minimize: sense = self.problem.objective.sense.minimize else: sense = self.problem.objective.sense.maximize self.problem.objective.set_sense(sense) self._cached_sense = minimize def build_problem(self, model): """ Create problem structure for a given model. Arguments: model : CBModel """ var_ids = list(model.reactions.keys()) lbs = [rxn.lb for rxn in model.reactions.values()] ubs = [rxn.ub for rxn in model.reactions.values()] var_types = [VarType.CONTINUOUS] * len(var_ids) self.add_variables(var_ids, lbs, ubs, var_types) constr_ids = list(model.metabolites.keys()) table = model.metabolite_reaction_lookup(force_recalculate=True) lhs = list(table.values()) senses = ['='] * len(constr_ids) rhs = [0] * len(constr_ids) self.add_constraints(constr_ids, lhs, senses, rhs) def solve(self, linear=None, quadratic=None, minimize=None, model=None, constraints=None, get_values=True, get_shadow_prices=False, get_reduced_costs=False, pool_size=0, pool_gap=None): """ Solve the optimization problem. Arguments: linear (dict): linear objective (optional) quadratic (dict): quadratic objective (optional) minimize (bool): solve a minimization problem (default: True) model (CBModel): model (optional, leave blank to reuse previous model structure) constraints (dict): additional constraints (optional) get_values (bool or list): set to false for speedup if you only care about the objective value (default: True) get_shadow_prices (bool): return shadow prices if available (default: False) get_reduced_costs (bool): return reduced costs if available (default: False) pool_size (int): calculate solution pool of given size (only for MILP problems) pool_gap (float): maximum relative gap for solutions in pool (optional) Returns: Solution: solution """ if model: self.build_problem(model) problem = self.problem if constraints: changed_lb, changed_ub = self.temporary_bounds(constraints) self.set_objective(linear, quadratic, minimize) # from datetime import datetime # self.write_to_file(f"{datetime.now()}.lp") #run the optimization if pool_size == 0: problem.solve() status = self.status_mapping.get(problem.solution.get_status(), Status.UNKNOWN) message = str(problem.solution.get_status_string()) if status == Status.OPTIMAL: fobj = problem.solution.get_objective_value() values, shadow_prices, reduced_costs = None, None, None if get_values: if isinstance(get_values, Iterable): get_values = list(get_values) values = OrderedDict( zip(get_values, problem.solution.get_values(get_values))) else: values = OrderedDict( zip(self.var_ids, problem.solution.get_values())) if get_shadow_prices: shadow_prices = OrderedDict( zip(self.constr_ids, problem.solution.get_dual_values(self.constr_ids))) if get_reduced_costs: reduced_costs = OrderedDict( zip(self.var_ids, problem.solution.get_reduced_costs(self.var_ids))) solution = Solution(status, message, fobj, values, shadow_prices, reduced_costs) else: solution = Solution(status, message) else: pool_pmap = { 'SolnPoolIntensity': problem.parameters.mip.pool.intensity, 'PopulateLim': problem.parameters.mip.limits.populate, 'SolnPoolCapacity': problem.parameters.mip.pool.capacity, 'SolnPoolReplace': problem.parameters.mip.pool.replace, 'SolnPoolGap': problem.parameters.mip.pool.relgap, 'SolnPoolAGap': problem.parameters.mip.pool.absgap } default_params = { 'SolnPoolIntensity': 3, 'PopulateLim': 10 * pool_size, 'SolnPoolCapacity': pool_size, 'SolnPoolReplace': 1 } for param, val in default_params.items(): pool_pmap[param].set(val) if pool_gap: pool_pmap['SolnPoolGap'].set(pool_gap) problem.populate_solution_pool() status = self.status_mapping.get(problem.solution.get_status(), Status.UNKNOWN) if status == Status.OPTIMAL or status == Status.UNKNOWN: solution = self.get_solution_pool(get_values) else: solution = [] if constraints: self.reset_bounds(changed_lb, changed_ub) return solution def temporary_bounds(self, constraints): lower_bounds, upper_bounds = {}, {} lb_new, ub_new = {}, {} def _dict_diff(dict1, dict2): return set(dict1.items()) - set(dict2.items()) for r_id, x in constraints.items(): if r_id in self.var_ids: lb, ub = x if isinstance(x, tuple) else (x, x) lower_bounds[r_id] = lb if lb is not None else -infinity upper_bounds[r_id] = ub if ub is not None else infinity else: warnings.warn( "Constrained variable '{}' not previously declared".format( r_id), RuntimeWarning) if lower_bounds != self._cached_lower_bounds: lb_new = _dict_diff(lower_bounds, self._cached_lower_bounds) if len(lb_new) > 0: self.problem.variables.set_lower_bounds(lb_new) if upper_bounds != self._cached_upper_bounds: ub_new = _dict_diff(upper_bounds, self._cached_upper_bounds) if len(ub_new) > 0: self.problem.variables.set_upper_bounds(ub_new) return lb_new, ub_new def get_solution_pool(self, get_values=True): """ Return a solution pool for MILP problems. Must be called after using solve with pool_size argument > 0. Arguments: get_values (bool or list): set to false for speedup if you only care about the objective value (default: True) Returns: list: list of Solution objects """ pool = self.problem.solution.pool solutions = [] for i in range(pool.get_num()): obj = pool.get_objective_value(i) # TODO: remove all OrderedDicts when migrating to python 3.7 # values = OrderedDict([(r_id, pool.get_values(i, r_id)) for r_id in self.var_ids]) if get_values: if isinstance(get_values, Iterable): get_values = list(get_values) values = dict( zip(get_values, pool.get_values(i, get_values))) else: values = dict(zip(self.var_ids, pool.get_values(i))) else: values = None sol = Solution(fobj=obj, values=values) solutions.append(sol) return solutions def reset_bounds(self, updated_lb, updated_ub): if updated_lb: lb_old = [(r_id, self._cached_lower_bounds[r_id]) for r_id, _ in updated_lb] self.problem.variables.set_lower_bounds(lb_old) if updated_ub: ub_old = [(r_id, self._cached_upper_bounds[r_id]) for r_id, _ in updated_ub] self.problem.variables.set_upper_bounds(ub_old) def set_lower_bounds(self, bounds_dict): self.problem.variables.set_lower_bounds([ (var_id, lb if lb is not None else -infinity) for var_id, lb in bounds_dict.items() ]) def set_upper_bounds(self, bounds_dict): self.problem.variables.set_lower_bounds([ (var_id, ub if ub is not None else infinity) for var_id, ub in bounds_dict.items() ]) def set_bounds(self, bounds_dict): self.problem.variables.set_lower_bounds([ (var_id, bounds[0] if bounds[0] is not None else -infinity) for var_id, bounds in bounds_dict.items() ]) self.problem.variables.set_upper_bounds([ (var_id, bounds[1] if bounds[1] is not None else infinity) for var_id, bounds in bounds_dict.items() ]) def update_coefficient(self, coeff, var_id, value): self.problem.linear_constraints.set_coefficients([(coeff, var_id, value)]) def update_coefficients(self, coefficients): self.problem.linear_constraints.set_coefficients(coefficients) def set_parameter(self, parameter, value): """ Set a parameter value for this optimization problem Arguments: parameter (Parameter): parameter type value (float): parameter value """ if parameter in self.parameter_mapping: self.parameter_mapping[parameter].set(value) else: raise Exception('Parameter unknown (or not yet supported).') def set_logging(self, enabled=False): """ Enable or disable log output: Arguments: enabled (bool): turn logging on (default: False) """ if enabled: self.problem.set_log_stream(sys.stdout) self.problem.set_error_stream(sys.stderr) self.problem.set_warning_stream(sys.stderr) self.problem.set_results_stream(sys.stdout) else: self.problem.set_log_stream(None) self.problem.set_error_stream(None) self.problem.set_warning_stream(None) self.problem.set_results_stream(None) def write_to_file(self, filename): """ Write problem to file: Arguments: filename (str): file path """ self.problem.write(filename)
def variability_legacy_cplex( model, variable_list=None, params=False, ): """Custom function to perform TVA on MIQC problem using gurobi. Parameters ---------- model : multitfa.core.tmodel multitfa model after thermodynamic constraints are added variable_list : List, optional List of variables to perform TVA on, by default None params : Bool, optional If True sets the Timelimit option to 300 sec and reduced the mip gap to 0.005 Returns ------- pd.DataFrame Dataframe of min max ranges of variables Raises ------ ValueError [description] """ # Instead of copying the whole model, just copy the cplex solver object by writing to a file and reading again. from cplex import Cplex, SparsePair tmp_dir = (os.path.normpath(os.path.dirname(os.path.abspath(__file__))) + os.sep + os.pardir + os.sep + "tmp") if not os.path.exists(tmp_dir): os.makedirs(tmp_dir) # Instantiate Cplex model cplex_model = Cplex() rand_str = "".join(choices(string.ascii_lowercase + string.digits, k=6)) # write cplex model to mps file and re read with tempfile.TemporaryDirectory() as td: temp_filename = os.path.join(td, rand_str + ".mps") model.cplex_interface.write(temp_filename) cplex_model.read(temp_filename) cplex_model.set_log_stream(None) cplex_model.set_error_stream(None) cplex_model.set_warning_stream(None) cplex_model.set_results_stream(None) if params: # print("lol") cplex_model.parameters.mip.tolerances.mipgap = 0.005 cplex_model.parameters.timelimit = 300 cplex_model.parameters.mip.limits.probetime = 300 # Make shorts for sense max_sense = cplex_model.objective.sense.maximize min_sense = cplex_model.objective.sense.minimize if variable_list == None: variables = model.cplex_interface.variables.get_names() else: variables = [var for var in variable_list] vars_list_cplex = cplex_model.variables.get_names() fluxes_min = np.empty(len(variables)) fluxes_max = np.empty(len(variables)) rxn_name = list() rxn_ids = [rxn.id for rxn in model.reactions] for i in range(len(variables)): # Reset objective vector for each iteration for varname in vars_list_cplex: cplex_model.objective.set_linear(varname, 0) # if the variable is reactions optimize for forward - reverse variables else optimize for the variable if variables[i] in rxn_ids: rxn = model.reactions.get_by_id(variables[i]) cplex_model.objective.set_linear([(rxn.forward_variable.name, 1), (rxn.reverse_variable.name, -1)]) else: cplex_model.objective.set_linear(variables[i], 1) rxn_name.append(variables[i]) # minimization cplex_model.objective.set_sense(min_sense) cplex_model.solve() objective_value = cplex_model.solution.get_objective_value() fluxes_min[i] = objective_value # maximiztion cplex_model.objective.set_sense(max_sense) cplex_model.solve() objective_value = cplex_model.solution.get_objective_value() fluxes_max[i] = objective_value return DataFrame({ "minimum": Series(index=rxn_name, data=fluxes_min), "maximum": Series(index=rxn_name, data=fluxes_max), })
#!/usr/bin/env python3 import cplex from cplex import Cplex from cplex.exceptions import CplexError import numpy as np import matplotlib.pyplot as plt mip_solver = Cplex() mip_solver.set_results_stream(None) mip_solver.set_warning_stream(None) mip_solver.set_error_stream(None) # mip_solver.parameters.threads.set(1) hidden_weights = [np.load("hidden_weights_1.npy"), np.load("hidden_weights_2.npy")] hidden_bias = [np.load("hidden_bias_1.npy"), np.load("hidden_bias_2.npy")] output_weights = np.load("output_weights.npy") output_bias = np.load("output_bias.npy") #mip_solver.objective.set_sense(mip_solver.objective.sense.minimize) input_dim = 28*28 hidden_nodes = [10,10] output_nodes = 10 # output variables mip_solver.variables.add( lb = [-cplex.infinity]*output_nodes, ub = [cplex.infinity]*output_nodes, types = "C"*output_nodes,
def _optimize_cplex(cobra_model, new_objective=None, objective_sense='maximize', min_norm=0, the_problem=None, tolerance_optimality=1e-6, tolerance_feasibility=1e-6, tolerance_integer=1e-9, tolerance_barrier=1e-8, error_reporting=None, print_solver_time=False, lp_method=1, lp_parallel=0, copy_problem=False, relax_b=None, quadratic_component=None, reuse_basis=True, update_problem_reaction_bounds=True): """Uses the ILOG/CPLEX (www.ibm.com/software/integration/optimization/cplex-optimizer/) optimizer to perform an optimization on cobra_model for the objective_coefficients in cobra_model._objective_coefficients based on the objective sense. cobra_model: A cobra.Model object new_objective: Reaction, String, or Integer referring to a reaction in cobra_model.reactions to set as the objective. Currently, only supports single objective coeffients. Will expand to include mixed objectives. objective_sense: 'maximize' or 'minimize' min_norm: not implemented the_problem: None or a problem object for the specific solver that can be used to hot start the next solution. tolerance_optimality: Solver tolerance for optimality. tolerance_feasibility: Solver tolerance for feasibility. error_reporting: None or True to disable or enable printing errors encountered when trying to find the optimal solution. print_solver_time: False or True. Indicates if the time to calculate the solution should be displayed. quadratic_component: None or scipy.sparse.dok of dim(len(cobra_model.reactions),len(cobra_model.reactions)) If not None: Solves quadratic programming problems for cobra_models of the form: minimize: 0.5 * x' * quadratic_component * x + cobra_model._objective_coefficients' * x such that, cobra_model._lower_bounds <= x <= cobra_model._upper_bounds cobra_model._S * x (cobra_model._constraint_sense) cobra_model._b reuse_basis: Boolean. If True and the_problem is a model object for the solver, attempt to hot start the solution. update_problem_reaction_bounds: Boolean. Set to True if you're providing the_problem and you've modified reaction bounds on your cobra_model since creating the_problem. Only necessary for CPLEX method for linear optimization: 0 = automatic 1 = primal simplex, 2 = dual simplex, 3 = network simplex, 4 = barrier, 5 = sifting, 6 = concurrent dual, barrier, and primal lp.solve() with Salmonella model: cold start: 0.05 seconds hot start: 0.05 seconds (slow due to copying the LP) """ if relax_b is not None: raise Exception('Need to reimplement constraint relaxation') from numpy import array, nan, zeros from cobra.flux_analysis.objective import update_objective from cobra.solvers.legacy import status_dict, variable_kind_dict if error_reporting == 'time' or print_solver_time: from time import time start_time = time() try: from cplex import Cplex, SparsePair variable_kind_dict = eval(variable_kind_dict['cplex']) status_dict = eval(status_dict['cplex']) except ImportError as e: import sys if 'wrong architecture' in e[0] and sys.maxsize > 2**32: print 'CPLEX python API is not 64-bit. please contact your IBM representative' else: print e if new_objective and new_objective != 'update problem': update_objective(cobra_model, new_objective) if the_problem == None or the_problem in ['return', 'setup', 'parallel'] \ or not isinstance(the_problem, Cplex): lp = Cplex() #Using the new objects #NOTE: This might be slow objective_coefficients = [] lower_bounds = [] upper_bounds = [] variable_names = [] variable_kinds = [] [(objective_coefficients.append(x.objective_coefficient), lower_bounds.append(x.lower_bound), upper_bounds.append(x.upper_bound), variable_names.append(x.id), variable_kinds.append(variable_kind_dict[x.variable_kind])) for x in cobra_model.reactions] #Cplex decides that the problem is a MIP if variable_kinds are supplied #even if there aren't any integers. if Cplex.variables.type.integer in variable_kinds: lp.variables.add(obj=objective_coefficients, lb=lower_bounds, ub=upper_bounds, names=variable_names, types=variable_kinds) else: lp.variables.add(obj=objective_coefficients, lb=lower_bounds, ub=upper_bounds, names=variable_names) if relax_b: raise Exception('need to reimplement relax_b') ## range_values = zeros(len(cobra_model.metabolites)) ## b_values = array([x._bound for x in cobra_model.metabolties]) ## for the_nonzero in list(b_values.nonzero()[0]): ## range_values[the_nonzero] = -relax_b constraint_sense = [] constraint_names = [] constraint_limits = [] [(constraint_sense.append(x._constraint_sense), constraint_names.append(x.id), constraint_limits.append(x._bound)) for x in cobra_model.metabolites] the_linear_expressions = [] #NOTE: This won't work with metabolites that aren't in any reaction for the_metabolite in cobra_model.metabolites: variable_list = [] coefficient_list = [] for the_reaction in the_metabolite._reaction: variable_list.append(the_reaction.id) coefficient_list.append( the_reaction._metabolites[the_metabolite]) the_linear_expressions.append( SparsePair(ind=variable_list, val=coefficient_list)) if quadratic_component is not None: if not hasattr(quadratic_component, 'todok'): raise Exception( 'quadratic component must be a scipy.sparse type array') quadratic_component_scaled = quadratic_component.todok() lp.parameters.emphasis.numerical.set(1) for k, v in quadratic_component_scaled.items(): lp.objective.set_quadratic_coefficients( int(k[0]), int(k[1]), v) if relax_b: lp.linear_constraints.add(lin_expr=the_linear_expressions, rhs=constraint_limits, range_values=list(range_values), senses=constraint_sense, names=constraint_names) else: lp.linear_constraints.add(lin_expr=the_linear_expressions, rhs=constraint_limits, senses=constraint_sense, names=constraint_names) if error_reporting == 'time': print 'setup new problem: ' + repr(time() - start_time) start_time = time() #Set the problem type as cplex doesn't appear to do this correctly problem_type = Cplex.problem_type.LP if Cplex.variables.type.integer in variable_kinds: if quadratic_component is not None: problem_type = Cplex.problem_type.MIQP else: problem_type = Cplex.problem_type.MILP elif quadratic_component is not None: problem_type = Cplex.problem_type.QP lp.set_problem_type(problem_type) else: if copy_problem: lp = Cplex(the_problem) if error_reporting == 'time': print 'copy problem: ' + repr(time() - start_time) start_time = time() else: lp = the_problem if new_objective: lp.objective.set_linear([(x.id, float(x.objective_coefficient)) for x in cobra_model.reactions]) if error_reporting == 'time': print 'set lp objective: ' + repr(time() - start_time) start_time = time() #SPEED THIS UP if update_problem_reaction_bounds: lp.variables.set_upper_bounds([(x.id, float(x.upper_bound)) for x in cobra_model.reactions]) lp.variables.set_lower_bounds([(x.id, float(x.lower_bound)) for x in cobra_model.reactions]) if error_reporting == 'time': print 'changed all bounds: ' + repr(time() - start_time) start_time = time() if objective_sense == 'maximize': lp.objective.set_sense(lp.objective.sense.maximize) else: lp.objective.set_sense(lp.objective.sense.minimize) if tolerance_optimality < 1e-10: lp.parameters.simplex.perturbation.constant.set(1) lp.parameters.simplex.pgradient.set(1) lp.parameters.emphasis.memory.set(1) #lp.parameters.simplex.tolerances.markowitz.set(.01) lp.parameters.advance.set(2) lp.parameters.simplex.tolerances.optimality.set(tolerance_optimality) lp.parameters.simplex.tolerances.feasibility.set(tolerance_feasibility) if lp.get_problem_type() in [ Cplex.problem_type.LP, Cplex.problem_type.MILP ]: lp.parameters.lpmethod.set(lp_method) elif lp.get_problem_type() in [ Cplex.problem_type.QP, Cplex.problem_type.MIQP ]: lp.parameters.qpmethod.set(lp_method) if lp_parallel > 1: lp.parameters.threads.set(lp_parallel) #lp.parameters.parallel.set(lp_parallel) lp.parameters.barrier.convergetol.set(tolerance_barrier) if the_problem == 'setup': return lp if not error_reporting: lp.set_results_stream(None) lp.set_warning_stream(None) if print_solver_time: start_time = time() if not isinstance(the_problem, Cplex): #TODO: set tolerance lp.solve() # Solve this LP with the simplex method. Takes about 0.2 s without hot start lp.status = lp.solution.status[lp.solution.get_status()] if lp.status in status_dict: status = status_dict[lp.status] else: status = 'failed' else: if isinstance(the_problem, Cplex) and reuse_basis: try: the_basis = the_problem.solution.basis.get_basis() lp.start.set_basis(the_basis[0], the_basis[1]) #TODO: Determine whether the primal or dual works best for the #problem of interest. For the ME matrix the primal appears to #work best lp_method = 1 lp.parameters.preprocessing.presolve.set(0) lp.parameters.lpmethod.set(lp_method) except: print 'no basis in the_problem' #TODO: set tolerance and time limit #lp.parameters.timelimit.set() lp.solve() #If the solver takes more than 0.1 s with a hot start it is likely stuck lp.status = lp.solution.status[lp.solution.get_status()] if lp.status in status_dict: status = status_dict[lp.status] else: status = 'failed' if status != 'optimal': #Cycle through the different solver options, if a solution is not found for lp_method in (1, 2, 3, 4, 5, 6): lp = optimize_cplex( cobra_model, new_objective=new_objective, objective_sense=objective_sense, min_norm=min_norm, the_problem=None, print_solver_time=print_solver_time, tolerance_optimality=tolerance_optimality, tolerance_feasibility=tolerance_feasibility, lp_method=lp_method, quadratic_component=quadratic_component)['the_problem'] lp.status = lp.solution.status[lp.solution.get_status()] if lp.status in status_dict: status = status_dict[lp.status] else: status = 'failed' if status == 'optimal': break if error_reporting == 'time': print 'solver time: ' + repr( time() - start_time) + ' with method ' + repr(lp_method) start_time = time() if print_solver_time: print 'cplex time: %f' % (time() - start_time) #TODO: It might be able to speed this up a little. if status == 'optimal': objective_value = lp.solution.get_objective_value() #This can be sped up a little x_dict = dict(zip(lp.variables.get_names(), lp.solution.get_values())) x = array(lp.solution.get_values()) x = x.reshape(x.shape[0], 1) #MIP's don't have duals if lp.get_problem_type() in (Cplex.problem_type.MIQP, Cplex.problem_type.MILP): y = y_dict = None else: y_dict = dict( zip(lp.linear_constraints.get_names(), lp.solution.get_dual_values())) y = array(lp.solution.get_dual_values()) y = y.reshape(y.shape[0], 1) else: x = y = x_dict = y_dict = objective_value = None if error_reporting: print 'cplex failed: %s' % lp.status cobra_model.solution = the_solution = Solution(objective_value, x=x, x_dict=x_dict, status=status, y=y, y_dict=y_dict) solution = {'the_problem': lp, 'the_solution': the_solution} return solution
def _optimize_cplex(cobra_model, new_objective=None, objective_sense='maximize', min_norm=0, the_problem=None, tolerance_optimality=1e-6, tolerance_feasibility=1e-6, tolerance_integer=1e-9, tolerance_barrier=1e-8,error_reporting=None, print_solver_time=False, lp_method=1, lp_parallel=0, copy_problem=False, relax_b=None, quadratic_component=None, reuse_basis=True, update_problem_reaction_bounds=True): """Uses the ILOG/CPLEX (www.ibm.com/software/integration/optimization/cplex-optimizer/) optimizer to perform an optimization on cobra_model for the objective_coefficients in cobra_model._objective_coefficients based on the objective sense. cobra_model: A cobra.Model object new_objective: Reaction, String, or Integer referring to a reaction in cobra_model.reactions to set as the objective. Currently, only supports single objective coeffients. Will expand to include mixed objectives. objective_sense: 'maximize' or 'minimize' min_norm: not implemented the_problem: None or a problem object for the specific solver that can be used to hot start the next solution. tolerance_optimality: Solver tolerance for optimality. tolerance_feasibility: Solver tolerance for feasibility. error_reporting: None or True to disable or enable printing errors encountered when trying to find the optimal solution. print_solver_time: False or True. Indicates if the time to calculate the solution should be displayed. quadratic_component: None or scipy.sparse.dok of dim(len(cobra_model.reactions),len(cobra_model.reactions)) If not None: Solves quadratic programming problems for cobra_models of the form: minimize: 0.5 * x' * quadratic_component * x + cobra_model._objective_coefficients' * x such that, cobra_model._lower_bounds <= x <= cobra_model._upper_bounds cobra_model._S * x (cobra_model._constraint_sense) cobra_model._b reuse_basis: Boolean. If True and the_problem is a model object for the solver, attempt to hot start the solution. update_problem_reaction_bounds: Boolean. Set to True if you're providing the_problem and you've modified reaction bounds on your cobra_model since creating the_problem. Only necessary for CPLEX method for linear optimization: 0 = automatic 1 = primal simplex, 2 = dual simplex, 3 = network simplex, 4 = barrier, 5 = sifting, 6 = concurrent dual, barrier, and primal lp.solve() with Salmonella model: cold start: 0.05 seconds hot start: 0.05 seconds (slow due to copying the LP) """ if relax_b is not None: raise Exception('Need to reimplement constraint relaxation') from numpy import array, nan, zeros from cobra.flux_analysis.objective import update_objective from cobra.solvers.legacy import status_dict, variable_kind_dict if error_reporting == 'time' or print_solver_time: from time import time start_time = time() try: from cplex import Cplex, SparsePair variable_kind_dict = eval(variable_kind_dict['cplex']) status_dict = eval(status_dict['cplex']) except ImportError as e: import sys if 'wrong architecture' in e[0] and sys.maxsize > 2**32: print 'CPLEX python API is not 64-bit. please contact your IBM representative' else: print e if new_objective and new_objective != 'update problem': update_objective(cobra_model, new_objective) if the_problem == None or the_problem in ['return', 'setup', 'parallel'] \ or not isinstance(the_problem, Cplex): lp = Cplex() #Using the new objects #NOTE: This might be slow objective_coefficients = [] lower_bounds = [] upper_bounds = [] variable_names = [] variable_kinds = [] [(objective_coefficients.append(x.objective_coefficient), lower_bounds.append(x.lower_bound), upper_bounds.append(x.upper_bound), variable_names.append(x.id), variable_kinds.append(variable_kind_dict[x.variable_kind])) for x in cobra_model.reactions] #Cplex decides that the problem is a MIP if variable_kinds are supplied #even if there aren't any integers. if Cplex.variables.type.integer in variable_kinds: lp.variables.add(obj=objective_coefficients, lb=lower_bounds, ub=upper_bounds, names=variable_names, types=variable_kinds) else: lp.variables.add(obj=objective_coefficients, lb=lower_bounds, ub=upper_bounds, names=variable_names) if relax_b: raise Exception('need to reimplement relax_b') ## range_values = zeros(len(cobra_model.metabolites)) ## b_values = array([x._bound for x in cobra_model.metabolties]) ## for the_nonzero in list(b_values.nonzero()[0]): ## range_values[the_nonzero] = -relax_b constraint_sense = [] constraint_names = [] constraint_limits = [] [(constraint_sense.append(x._constraint_sense), constraint_names.append(x.id), constraint_limits.append(x._bound)) for x in cobra_model.metabolites] the_linear_expressions = [] #NOTE: This won't work with metabolites that aren't in any reaction for the_metabolite in cobra_model.metabolites: variable_list = [] coefficient_list = [] for the_reaction in the_metabolite._reaction: variable_list.append(the_reaction.id) coefficient_list.append(the_reaction._metabolites[the_metabolite]) the_linear_expressions.append(SparsePair(ind=variable_list, val=coefficient_list)) if quadratic_component is not None: if not hasattr(quadratic_component, 'todok'): raise Exception('quadratic component must be a scipy.sparse type array') quadratic_component_scaled = quadratic_component.todok() lp.parameters.emphasis.numerical.set(1) for k, v in quadratic_component_scaled.items(): lp.objective.set_quadratic_coefficients(int(k[0]), int(k[1]), v) if relax_b: lp.linear_constraints.add(lin_expr=the_linear_expressions, rhs=constraint_limits, range_values=list(range_values), senses=constraint_sense, names=constraint_names) else: lp.linear_constraints.add(lin_expr=the_linear_expressions, rhs=constraint_limits, senses=constraint_sense, names=constraint_names) if error_reporting == 'time': print 'setup new problem: ' + repr(time()-start_time) start_time = time() #Set the problem type as cplex doesn't appear to do this correctly problem_type = Cplex.problem_type.LP if Cplex.variables.type.integer in variable_kinds: if quadratic_component is not None: problem_type = Cplex.problem_type.MIQP else: problem_type = Cplex.problem_type.MILP elif quadratic_component is not None: problem_type = Cplex.problem_type.QP lp.set_problem_type(problem_type) else: if copy_problem: lp = Cplex(the_problem) if error_reporting == 'time': print 'copy problem: ' + repr(time()-start_time) start_time = time() else: lp = the_problem if new_objective: lp.objective.set_linear([(x.id, float(x.objective_coefficient)) for x in cobra_model.reactions]) if error_reporting == 'time': print 'set lp objective: ' + repr(time()-start_time) start_time = time() #SPEED THIS UP if update_problem_reaction_bounds: lp.variables.set_upper_bounds([(x.id, float(x.upper_bound)) for x in cobra_model.reactions]) lp.variables.set_lower_bounds([(x.id, float(x.lower_bound)) for x in cobra_model.reactions]) if error_reporting == 'time': print 'changed all bounds: ' + repr(time()-start_time) start_time = time() if objective_sense == 'maximize': lp.objective.set_sense(lp.objective.sense.maximize) else: lp.objective.set_sense(lp.objective.sense.minimize) if tolerance_optimality < 1e-10: lp.parameters.simplex.perturbation.constant.set(1) lp.parameters.simplex.pgradient.set(1) lp.parameters.emphasis.memory.set(1) #lp.parameters.simplex.tolerances.markowitz.set(.01) lp.parameters.advance.set(2) lp.parameters.simplex.tolerances.optimality.set(tolerance_optimality) lp.parameters.simplex.tolerances.feasibility.set(tolerance_feasibility) if lp.get_problem_type() in [Cplex.problem_type.LP, Cplex.problem_type.MILP]: lp.parameters.lpmethod.set(lp_method) elif lp.get_problem_type() in [Cplex.problem_type.QP, Cplex.problem_type.MIQP]: lp.parameters.qpmethod.set(lp_method) if lp_parallel > 1: lp.parameters.threads.set(lp_parallel) #lp.parameters.parallel.set(lp_parallel) lp.parameters.barrier.convergetol.set(tolerance_barrier) if the_problem == 'setup': return lp if not error_reporting: lp.set_results_stream(None) lp.set_warning_stream(None) if print_solver_time: start_time = time() if not isinstance(the_problem, Cplex): #TODO: set tolerance lp.solve() # Solve this LP with the simplex method. Takes about 0.2 s without hot start lp.status = lp.solution.status[lp.solution.get_status()] if lp.status in status_dict: status = status_dict[lp.status] else: status = 'failed' else: if isinstance(the_problem, Cplex) and reuse_basis: try: the_basis = the_problem.solution.basis.get_basis() lp.start.set_basis(the_basis[0],the_basis[1]) #TODO: Determine whether the primal or dual works best for the #problem of interest. For the ME matrix the primal appears to #work best lp_method = 1 lp.parameters.preprocessing.presolve.set(0) lp.parameters.lpmethod.set(lp_method) except: print 'no basis in the_problem' #TODO: set tolerance and time limit #lp.parameters.timelimit.set() lp.solve() #If the solver takes more than 0.1 s with a hot start it is likely stuck lp.status = lp.solution.status[lp.solution.get_status()] if lp.status in status_dict: status = status_dict[lp.status] else: status = 'failed' if status != 'optimal': #Cycle through the different solver options, if a solution is not found for lp_method in (1, 2, 3, 4, 5, 6): lp = optimize_cplex(cobra_model, new_objective=new_objective, objective_sense=objective_sense, min_norm=min_norm, the_problem=None, print_solver_time=print_solver_time, tolerance_optimality=tolerance_optimality, tolerance_feasibility=tolerance_feasibility, lp_method=lp_method, quadratic_component=quadratic_component)['the_problem'] lp.status = lp.solution.status[lp.solution.get_status()] if lp.status in status_dict: status = status_dict[lp.status] else: status = 'failed' if status == 'optimal': break if error_reporting == 'time': print 'solver time: ' + repr(time()-start_time) + ' with method ' + repr(lp_method) start_time = time() if print_solver_time: print 'cplex time: %f'%(time() - start_time) x = [] x_dict = {} #TODO: It might be able to speed this up a little. if status == 'optimal': objective_value = lp.solution.get_objective_value() #This can be sped up a little x_dict = dict(zip(lp.variables.get_names(), lp.solution.get_values())) x = array(lp.solution.get_values()) x = x.reshape(x.shape[0],1) #MIP's don't have duals if lp.get_problem_type() in (Cplex.problem_type.MIQP, Cplex.problem_type.MILP): y = y_dict = None else: y_dict = dict(zip(lp.linear_constraints.get_names(), lp.solution.get_dual_values())) y = array(lp.solution.get_dual_values()) y = y.reshape(y.shape[0],1) else: x = y = x_dict = y_dict = objective_value = None if error_reporting: print 'cplex failed: %s'%lp.status the_solution = Solution(objective_value, x=x, x_dict=x_dict, status=status, y=y, y_dict=y_dict) solution = {'the_problem': lp, 'the_solution': the_solution} return solution
def qps_cplex(H, c, A, l, u, xmin, xmax, x0, opt): """Quadratic Program Solver based on CPLEX. A wrapper function providing a PYPOWER standardized interface for using C{cplexqp} or C{cplexlp} to solve the following QP (quadratic programming) problem:: min 1/2 X'*H*x + c'*x x subject to:: l <= A*x <= u (linear constraints) xmin <= x <= xmax (variable bounds) Inputs (all optional except C{H}, C{c}, C{A} and C{l}): - C{H} : matrix (possibly sparse) of quadratic cost coefficients - C{c} : vector of linear cost coefficients - C{A, l, u} : define the optional linear constraints. Default values for the elements of L and U are -Inf and Inf, respectively. - C{xmin, xmax} : optional lower and upper bounds on the C{x} variables, defaults are -Inf and Inf, respectively. - C{x0} : optional starting value of optimization vector C{x} - C{opt} : optional options structure with the following fields, all of which are also optional (default values shown in parentheses) - C{verbose} (0) - controls level of progress output displayed - 0 = no progress output - 1 = some progress output - 2 = verbose progress output - C{cplex_opt} - options dict for CPLEX, value in verbose overrides these options - C{problem} : The inputs can alternatively be supplied in a single C{problem} dict with fields corresponding to the input arguments described above: C{H, c, A, l, u, xmin, xmax, x0, opt} Outputs: - C{x} : solution vector - C{f} : final objective function value - C{exitflag} : CPLEXQP/CPLEXLP exit flag (see C{cplexqp} and C{cplexlp} documentation for details) - C{output} : CPLEXQP/CPLEXLP output dict (see C{cplexqp} and C{cplexlp} documentation for details) - C{lmbda} : dict containing the Langrange and Kuhn-Tucker multipliers on the constraints, with fields: - mu_l - lower (left-hand) limit on linear constraints - mu_u - upper (right-hand) limit on linear constraints - lower - lower bound on optimization variables - upper - upper bound on optimization variables @author: Ray Zimmerman (PSERC Cornell) """ ##----- input argument handling ----- ## gather inputs if isinstance(H, dict): ## problem struct p = H if 'opt' in p: opt = p['opt'] if 'x0' in p: x0 = p['x0'] if 'xmax' in p: xmax = p['xmax'] if 'xmin' in p: xmin = p['xmin'] if 'u' in p: u = p['u'] if 'l' in p: l = p['l'] if 'A' in p: A = p['A'] if 'c' in p: c = p['c'] if 'H' in p: H = p['H'] else: ## individual args assert H is not None assert c is not None assert A is not None assert l is not None if opt is None: opt = {} # if x0 is None: # x0 = array([]) # if xmax is None: # xmax = array([]) # if xmin is None: # xmin = array([]) ## define nx, set default values for missing optional inputs if len(H) == 0 or not any(any(H)): if len(A) == 0 and len(xmin) == 0 and len(xmax) == 0: stderr.write( 'qps_cplex: LP problem must include constraints or variable bounds\n' ) else: if len(A) > 0: nx = shape(A)[1] elif len(xmin) > 0: nx = len(xmin) else: # if len(xmax) > 0 nx = len(xmax) else: nx = shape(H)[0] if len(c) == 0: c = zeros(nx) if len(A) > 0 and (len(l) == 0 or all(l == -Inf)) and \ (len(u) == 0 or all(u == Inf)): A = None ## no limits => no linear constraints nA = shape(A)[0] ## number of original linear constraints if len(u) == 0: ## By default, linear inequalities are ... u = Inf * ones(nA) ## ... unbounded above and ... if len(l) == 0: l = -Inf * ones(nA) ## ... unbounded below. if len(xmin) == 0: ## By default, optimization variables are ... xmin = -Inf * ones(nx) ## ... unbounded below and ... if len(xmax) == 0: xmax = Inf * ones(nx) ## ... unbounded above. if len(x0) == 0: x0 = zeros(nx) ## default options if 'verbose' in opt: verbose = opt['verbose'] else: verbose = 0 #if 'max_it' in opt: # max_it = opt['max_it'] #else: # max_it = 0 ## split up linear constraints ieq = find(abs(u - l) <= EPS) ## equality igt = find(u >= 1e10 & l > -1e10) ## greater than, unbounded above ilt = find(l <= -1e10 & u < 1e10) ## less than, unbounded below ibx = find((abs(u - l) > EPS) & (u < 1e10) & (l > -1e10)) Ae = A[ieq, :] be = u[ieq] Ai = r_[A[ilt, :], -A[igt, :], A[ibx, :] - A[ibx, :]] bi = r_[u[ilt], -l[igt], u[ibx], -l[ibx]] ## grab some dimensions nlt = len(ilt) ## number of upper bounded linear inequalities ngt = len(igt) ## number of lower bounded linear inequalities nbx = len(ibx) ## number of doubly bounded linear inequalities ## set up options struct for CPLEX if 'cplex_opt' in opt: cplex_opt = cplex_options(opt['cplex_opt']) else: cplex_opt = cplex_options cplex = Cplex('null') vstr = cplex.getVersion s, e, tE, m, t = re.compile(vstr, '(\d+\.\d+)\.') vnum = int(t[0][0]) vrb = max([0, verbose - 1]) cplex_opt['barrier']['display'] = vrb cplex_opt['conflict']['display'] = vrb cplex_opt['mip']['display'] = vrb cplex_opt['sifting']['display'] = vrb cplex_opt['simplex']['display'] = vrb cplex_opt['tune']['display'] = vrb if vrb and (vnum > 12.2): cplex_opt['diagnostics'] = 'on' #if max_it: # cplex_opt. ## not sure what to set here if len(Ai) == 0 and len(Ae) == 0: unconstrained = 1 Ae = sparse((1, nx)) be = 0 else: unconstrained = 0 ## call the solver if verbose: methods = [ 'default', 'primal simplex', 'dual simplex', 'network simplex', 'barrier', 'sifting', 'concurrent' ] if len(H) == 0 or not any(any(H)): if verbose: stdout.write('CPLEX Version %s -- %s LP solver\n' % (vstr, methods[cplex_opt['lpmethod'] + 1])) x, f, eflag, output, lam = \ cplexlp(c, Ai, bi, Ae, be, xmin, xmax, x0, cplex_opt) else: if verbose: stdout.write('CPLEX Version %s -- %s QP solver\n' % (vstr, methods[cplex_opt['qpmethod'] + 1])) ## ensure H is numerically symmetric if H != H.T: H = (H + H.T) / 2 x, f, eflag, output, lam = \ cplexqp(H, c, Ai, bi, Ae, be, xmin, xmax, x0, cplex_opt) ## check for empty results (in case optimization failed) if len(x) == 0: x = NaN * zeros(nx) if len(f) == 0: f = NaN if len(lam) == 0: lam['ineqlin'] = NaN * zeros(len(bi)) lam['eqlin'] = NaN * zeros(len(be)) lam['lower'] = NaN * zeros(nx) lam['upper'] = NaN * zeros(nx) mu_l = NaN * zeros(nA) mu_u = NaN * zeros(nA) else: mu_l = zeros(nA) mu_u = zeros(nA) if unconstrained: lam['eqlin'] = array([]) ## negate prices depending on version if vnum < 12.3: lam['eqlin'] = -lam['eqlin'] lam['ineqlin'] = -lam['ineqlin'] ## repackage lambdas kl = find(lam.eqlin < 0) ## lower bound binding ku = find(lam.eqlin > 0) ## upper bound binding mu_l[ieq[kl]] = -lam['eqlin'][kl] mu_l[igt] = lam['ineqlin'][nlt + arange(ngt)] mu_l[ibx] = lam['ineqlin'][nlt + ngt + nbx + arange(nbx)] mu_u[ieq[ku]] = lam['eqlin'][ku] mu_u[ilt] = lam['ineqlin'][:nlt] mu_u[ibx] = lam['ineqlin'][nlt + ngt + arange(nbx)] lmbda = { 'mu_l': mu_l, 'mu_u': mu_u, 'lower': lam.lower, 'upper': lam.upper } return x, f, eflag, output, lmbda
def create_problem(cobra_model, quadratic_component=None, **kwargs): """Solver-specific method for constructing a solver problem from a cobra.Model. This can be tuned for performance using kwargs """ # Process parameter defaults the_parameters = parameter_defaults if kwargs: the_parameters = parameter_defaults.copy() the_parameters.update(kwargs) if 'relax_b' in the_parameters: relax_b = the_parameters.pop("relax_b") warn('need to reimplement relax_b') relax_b = False else: relax_b = False # Begin problem creation lp = Cplex() for k, v in iteritems(the_parameters): set_parameter(lp, k, v) objective_coefficients = [float(x.objective_coefficient) for x in cobra_model.reactions] lower_bounds = [float(x.lower_bound) for x in cobra_model.reactions] upper_bounds = [float(x.upper_bound) for x in cobra_model.reactions] variable_names = cobra_model.reactions.list_attr("id") variable_kinds = [variable_kind_dict[x.variable_kind] for x in cobra_model.reactions] # Cplex decides that the problem is a MIP if variable_kinds are supplied # even if there aren't any integers. if variable_kind_dict['integer'] in variable_kinds: lp.variables.add(obj=objective_coefficients, lb=lower_bounds, ub=upper_bounds, names=variable_names, types=variable_kinds) else: lp.variables.add(obj=objective_coefficients, lb=lower_bounds, ub=upper_bounds, names=variable_names) ## if relax_b: ## range_values = zeros(len(cobra_model.metabolites)) ## b_values = array([x._bound for x in cobra_model.metabolties]) ## for the_nonzero in list(b_values.nonzero()[0]): ## range_values[the_nonzero] = -relax_b constraint_sense = [] constraint_names = [] constraint_limits = [] [(constraint_sense.append(x._constraint_sense), constraint_names.append(x.id), constraint_limits.append(float(x._bound))) for x in cobra_model.metabolites] the_linear_expressions = [] #NOTE: This won't work with metabolites that aren't in any reaction for the_metabolite in cobra_model.metabolites: variable_list = [] coefficient_list = [] for the_reaction in the_metabolite._reaction: variable_list.append(the_reaction.id) coefficient_list.append(float(the_reaction._metabolites[the_metabolite])) the_linear_expressions.append(SparsePair(ind=variable_list, val=coefficient_list)) # Set objective to quadratic program if quadratic_component is not None: set_quadratic_objective(lp, quadratic_component) if relax_b: lp.linear_constraints.add(lin_expr=the_linear_expressions, rhs=constraint_limits, range_values=list(range_values), senses=constraint_sense, names=constraint_names) else: lp.linear_constraints.add(lin_expr=the_linear_expressions, rhs=constraint_limits, senses=constraint_sense, names=constraint_names) #Set the problem type as cplex doesn't appear to do this correctly problem_type = Cplex.problem_type.LP if Cplex.variables.type.integer in variable_kinds: if quadratic_component is not None: problem_type = Cplex.problem_type.MIQP else: problem_type = Cplex.problem_type.MILP elif quadratic_component is not None: problem_type = Cplex.problem_type.QP lp.set_problem_type(problem_type) return(lp)
from cplex import Cplex, infinity from cplex.exceptions import CplexError problem = Cplex() problem.objective.set_sense(problem.objective.sense.minimize) objective = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] constraints_matrix = [ [1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0], [1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0], [0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0], [0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0], [0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0], ] col_names = [ "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12" ] row_names = [ "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "c10", "c11", "c12" ]
def fair_partial_assignment_lp_solver(df, centers, color_flag, alpha, beta, cost_fun_string): # There are primarily five steps: # 1. Initiate a model for cplex # 2. Declare if it is minimization or maximization problem # 3. Add variables to the model. The variables are generally named. # The upper bounds and lower bounds on the range for the variables # are also mentioned at this stage. The coefficient of the objective # functions are also entered at this step # 4. Add the constraints to the model. The constraint matrix, denoted by A, # can be added in three ways - row wise, column wise or non-zero entry wise. # 5. Finally, call the solver. # Step 1. Initiate a model for cplex. print("Initializing Cplex model") problem = Cplex() # Step 2. Declare that this is a minimization problem problem.objective.set_sense(problem.objective.sense.minimize) # Step 3. Declare and add variables to the model. The function # prepare_to_add_variables (points, center) prepares all the # required information for this stage. # # objective: a list of coefficients (float) in the linear objective function # lower bounds: a list of floats containing the lower bounds for each variable # upper bounds: a list of floats containing the upper bounds for each variable # variable_name: a list of strings that contains the name of the variables print("Starting to add variables...") print("HERE???") t1 = time.monotonic() objective, lower_bounds, upper_bounds, variable_names = prepare_to_add_variables( df, centers, cost_fun_string) problem.variables.add(obj=objective, lb=lower_bounds, ub=upper_bounds, names=variable_names) t2 = time.monotonic() print("Completed. Time for creating and adding variable = {}".format(t2 - t1)) # Step 4. Declare and add constraints to the model. # There are few ways of adding constraints: rwo wise, col wise and non-zero entry wise. # Assume the constraint matrix is A. We add the constraints row wise. # The function prepare_to_add_constraints_by_entry(points,center,colors,alpha,beta) # prepares the required data for this step. # # constraints_row: Encoding of each row of the constraint matrix # senses: a list of strings that identifies whether the corresponding constraint is # an equality or inequality. "E" : equals to (=), "L" : less than (<=), "G" : greater than equals (>=) # rhs: a list of floats corresponding to the rhs of the constraints. # constraint_names: a list of string corresponding to the name of the constraint print("Starting to add constraints...") t1 = time.monotonic() objects_returned = prepare_to_add_constraints(df, centers, color_flag, beta, alpha) constraints_row, senses, rhs, constraint_names = objects_returned problem.linear_constraints.add(lin_expr=constraints_row, senses=senses, rhs=rhs, names=constraint_names) t2 = time.monotonic() print( "Completed. Time for creating and adding constraints = {}".format(t2 - t1)) # Optional: We can set various parameters to optimize the performance of the lp solver # As an example, the following sets barrier method as the lp solving method # The other available methods are: auto, primal, dual, sifting, concurrent #problem.parameters.lpmethod.set(problem.parameters.lpmethod.values.barrier) return problem, objective
def solveit(max_duration, ads): # get the winners winners, losers = select_winners(ads, max_duration) # add a fake winner if needed. total_dur = sum([winner.duration for winner in winners]) if total_dur < max_duration: winners.add(Ad(bid = 0.0001, duration =max_duration - total_dur)) # get the upper contour for the losers bar = [i[1] for i in get_coutour(losers, max_duration, True)] print len(winners) winners = sorted(winners, key = lambda winner: (winner.duration, winner.bid)) id2num = {id(winner):k for k, winner in enumerate(winners)} prob = Cplex() lb = [bar[winner.duration] for winner in winners] ub = [winner.bid for winner in winners] prob.objective.set_sense(prob.objective.sense.minimize) obj = [1] * len(winners) prob.variables.add(obj, lb, ub) # monotone requirement (optional) # for i, x in enumerate(winners): # ceil = 1e9 # for k, y in enumerate(winners[i+1:]): # j = i + 1 + k # if y.bid == x.bid: # if x.duration == y.duration: # prob.linear_constraints.add([[[i, j], [1,-1]]], ['E'], [0]) # else: # prob.linear_constraints.add([[[i, j],[1,-1]]],['L'],[0]) # break # if y.bid > x.bid and y.bid < ceil: # ceil = y.bid # prob.linear_constraints.add([[[i,j],[1,-1]]],['L'],[0]) n_iteration = 0 last_obj = -1 while True: n_iteration += 1 prob.solve() obj = prob.solution.get_objective_value() last_obj = obj prices = prob.solution.get_values() for i, winner in enumerate(winners): winner.price = prices[i] lower_coutour = get_coutour(winners, max_duration, False) finish = True for k, tmp in enumerate(lower_coutour): ads, min_value = tmp if len(ads) == 0: continue if min_value < bar[k]: tmp = [id2num[id(ad)] for ad in ads] print prob.linear_constraints.get_num() prob.linear_constraints.add([[[id2num[id(ad)] for ad in ads], [1] * len(ads)]],\ ['G'], [bar[k]]) finish = False if finish: break return sum(prob.solution.get_values()), len(winners), n_iteration