def invert(self, results, inverse_data): # model = results["model"] attr = {} if s.SOLVE_TIME in results: attr[s.SOLVE_TIME] = results[s.SOLVE_TIME] attr[s.NUM_ITERS] = \ int(results['bariter']) \ if not inverse_data[XPRESS.IS_MIP] \ else 0 status_map_lp, status_map_mip = get_status_maps() if results['status'] == 'solver_error': status = 'solver_error' elif 'mip_' in results['getProbStatusString']: status = status_map_mip[results['status']] else: status = status_map_lp[results['status']] if status in s.SOLUTION_PRESENT: # Get objective value opt_val = results['getObjVal'] + inverse_data[s.OFFSET] # Get solution x = np.array(results['getSolution']) primal_vars = { XPRESS.VAR_ID: intf.DEFAULT_INTF.const_to_matrix(np.array(x)) } # Only add duals if not a MIP. dual_vars = None if not inverse_data[XPRESS.IS_MIP]: y = -np.array(results['getDual']) dual_vars = {XPRESS.DUAL_VAR_ID: y} else: primal_vars = None dual_vars = None opt_val = np.inf if status == s.UNBOUNDED: opt_val = -np.inf return Solution(status, opt_val, primal_vars, dual_vars, attr)
def solve_via_data(self, data, warm_start: bool, verbose: bool, solver_opts, solver_cache=None): import xpress as xp # Objective function: 1/2 x' P x + q'x Q = data[s.P] # objective quadratic coefficients q = data[s.Q] # objective linear coefficient (size n_var) # Equations, Ax = b A = data[s.A] # linear coefficient matrix b = data[s.B] # rhs n_var = data['n_var'] n_eq = data['n_eq'] self.prob_ = xp.problem() # qp_solver has the following format: # # minimize 1/2 x' P x + q' x # subject to A x = b # F x <= g # # Instead of combining A and F to call loadproblem() once # (which is inefficient due to a necessary Python loop), call # loadproblem() for A and then use addrow() mstart = makeMstart(A, n_var, 1) if len(Q.data) != 0: # Q matrix is input via row/col indices and value, but only # for the upper triangle. We just make it symmetric and twice # itself, then, just remove all lower-triangular elements. Q += Q.transpose() Q /= 2 Q = Q.tocoo() mqcol1 = Q.row[Q.row <= Q.col] mqcol2 = Q.col[Q.row <= Q.col] dqe = Q.data[Q.row <= Q.col] else: mqcol1, mqcol2, dqe = [], [], [] colnames = ['x_{0:09d}'.format(i) for i in range(n_var)] rownames = ['eq_{0:09d}'.format(i) for i in range(n_eq)] if verbose: self.prob_.controls.miplog = 2 self.prob_.controls.lplog = 1 self.prob_.controls.outputlog = 1 else: self.prob_.controls.miplog = 0 self.prob_.controls.lplog = 0 self.prob_.controls.outputlog = 0 self.prob_.controls.xslp_log = -1 self.prob_.loadproblem( probname='CVX_xpress_qp', # constraint types qrtypes=['E'] * n_eq, rhs=b, # rhs range=None, # range obj=q, # obj coeff mstart=mstart, # mstart mnel=None, # mnel (unused) # linear coefficients mrwind=A.indices[A.data != 0], # row indices dmatval=A.data[A.data != 0], # coefficients dlb=[-xp.infinity] * len(q), # lower bound dub=[xp.infinity] * len(q), # upper bound # quadratic objective (only upper triangle) mqcol1=mqcol1, mqcol2=mqcol2, dqe=dqe, # binary and integer variables qgtype=['B'] * len(data[s.BOOL_IDX]) + ['I'] * len(data[s.INT_IDX]), mgcols=data[s.BOOL_IDX] + data[s.INT_IDX], # variables' and constraints' names colnames=colnames, rownames=rownames) # The problem currently has the quadratic objective function # and the linear equations. Add the linear inequalities # # Fx <= g n_ineq = data['n_ineq'] if n_ineq > 0: F = data[s.F].tocsr( ) # linear coefficient matrix, converted to row-major g = data[s.G] # rhs mstartIneq = makeMstart(F, n_ineq, 0) # ifCol=0 --> check rows rownames_ineq = ['ineq_{0:09d}'.format(i) for i in range(n_ineq)] self.prob_.addrows( # constraint types qrtype=['L'] * n_ineq, # inequalities sign rhs=g, # rhs mstart=mstartIneq, # starting indices mclind=F.indices[F.data != 0], # column indices dmatval=F.data[F.data != 0], # coefficient names=rownames_ineq) # row names # Set options # # The parameter solver_opts is a dictionary that contains only # one key, 'solver_opt', and its value is a dictionary # {'control': value}, matching perfectly the format used by # the Xpress Python interface. # Set options if compatible with Xpress problem control names self.prob_.setControl({ i: solver_opts[i] for i in solver_opts if i in xp.controls.__dict__ }) if 'bargaptarget' not in solver_opts.keys(): self.prob_.controls.bargaptarget = 1e-30 if 'feastol' not in solver_opts.keys(): self.prob_.controls.feastol = 1e-9 # Solve problem results_dict = {"model": self.prob_} try: # If option given, write file before solving if 'write_mps' in solver_opts.keys(): self.prob_.write(solver_opts['write_mps']) self.prob_.solve() results_dict[s.SOLVE_TIME] = self.prob_.attributes.time except xp.SolverError: # Error in the solution results_dict["status"] = s.SOLVER_ERROR else: results_dict['status'] = self.prob_.getProbStatus() results_dict[ 'getProbStatusString'] = self.prob_.getProbStatusString() results_dict['obj_value'] = self.prob_.getObjVal() try: results_dict[s.PRIMAL] = np.array(self.prob_.getSolution()) except xp.SolverError: results_dict[s.PRIMAL] = np.zeros(self.prob_.attributes.ncol) status_map_lp, status_map_mip = get_status_maps() if results_dict['status'] == 'solver_error': status = 'solver_error' elif 'mip_' in results_dict['getProbStatusString']: status = status_map_mip[results_dict['status']] else: status = status_map_lp[results_dict['status']] results_dict['bariter'] = self.prob_.attributes.bariter results_dict[ 'getProbStatusString'] = self.prob_.getProbStatusString() if status in s.SOLUTION_PRESENT: results_dict['getObjVal'] = self.prob_.getObjVal() results_dict['getSolution'] = self.prob_.getSolution() if not (data[s.BOOL_IDX] or data[s.INT_IDX]): results_dict['getDual'] = self.prob_.getDual() del self.prob_ return results_dict
def solve_via_data(self, data, warm_start, verbose, solver_opts, solver_cache=None): import xpress as xp # Objective function: 1/2 x' P x + q'x Q = data[s.P] # objective quadratic coefficients q = data[s.Q] # objective linear coefficient (size n_var) # Equations, Ax = b A = data[s.A] # linear coefficient matrix b = data[s.B] # rhs # Inequalities, Fx <= g F = data[s.F] # linear coefficient matrix g = data[s.G] # rhs n_var = data['n_var'] n_eq = data['n_eq'] n_ineq = data['n_ineq'] self.prob_ = xp.problem() # qp_solver has the following format: # # minimize 1/2 x' P x + q' x # subject to A x = b # F x <= g mstartA = makeMstart(A, n_var, 1) mstartF = makeMstart(F, n_var, 1) # index start is simply the sum of the two mstarts for A and F mstart = mstartA + mstartF # In order to arrange (A;F) as a single matrix, we have to # concatenate all indices. I prefer not to do a vstack of A # and F as it might increase memory usage a lot. # Begin by creating two vectors of zeros for indices and # coefficients mrwind = np.zeros(mstart[n_var], dtype=np.int64) dmatval = np.zeros(mstart[n_var], dtype=np.float64) # Fill in mrwind and dmatval by alternately drawing from # indices/coeff of A and F. for i in range(n_var): nElemA = mstartA[i + 1] - mstartA[ i] # number of elements of A in this column nElemF = mstartF[i + 1] - mstartF[ i] # number of elements of F in this column if nElemA: mrwind[mstart[i]:mstart[i] + nElemA] = \ A.indices[A.data != 0][mstartA[i]:mstartA[i+1]] dmatval[mstart[i]:mstart[i] + nElemA] = \ A.data[A.data != 0][mstartA[i]:mstartA[i+1]] if nElemF: mrwind[mstart[i] + nElemA: mstart[i] + nElemA + nElemF] = \ n_eq + F.indices[F.data != 0][mstartF[i]:mstartF[i+1]] dmatval[mstart[i] + nElemA: mstart[i] + nElemA + nElemF] = \ F.data[F.data != 0][mstartF[i]:mstartF[i+1]] mstart[i] = mstartA[i] + mstartF[i] # The last value of mstart must be the total number of # coefficients. mstart[n_var] = mstartA[n_var] + mstartF[n_var] # Q matrix is input via row/col indices and value, but only # for the upper triangle. We just make it symmetric and twice # itself, then, just remove all lower-triangular elements. Q += Q.transpose() Q /= 2 Q = Q.tocoo() mqcol1 = Q.row[Q.row <= Q.col] mqcol2 = Q.col[Q.row <= Q.col] dqe = Q.data[Q.row <= Q.col] colnames = ['x_{0:09d}'.format(i) for i in range(n_var)] rownames = ['eq_{0:09d}'.format(i) for i in range(n_eq)] + \ ['ineq_{0:09d}'.format(i) for i in range(n_ineq)] if verbose: self.prob_.controls.miplog = 2 self.prob_.controls.lplog = 1 self.prob_.controls.outputlog = 1 else: self.prob_.controls.miplog = 0 self.prob_.controls.lplog = 0 self.prob_.controls.outputlog = 0 self.prob_.controls.xslp_log = -1 self.prob_.loadproblem( probname='CVX_xpress_qp', qrtypes=['E'] * n_eq + ['L'] * n_ineq, rhs=list(b) + list(g), range=None, obj=q, mstart=mstart, mnel=None, # linear coefficients mrwind=mrwind, dmatval=dmatval, # variable bounds dlb=[-xp.infinity] * n_var, dub=[xp.infinity] * n_var, # quadratic objective (only upper triangle) mqcol1=mqcol1, mqcol2=mqcol2, dqe=dqe, # binary and integer variables qgtype=['B'] * len(data[s.BOOL_IDX]) + ['I'] * len(data[s.INT_IDX]), mgcols=data[s.BOOL_IDX] + data[s.INT_IDX], # variables' and constraints' names colnames=colnames, rownames=rownames) # Set options # # The parameter solver_opts is a dictionary that contains only # one key, 'solver_opt', and its value is a dictionary # {'control': value}, matching perfectly the format used by # the Xpress Python interface. # Set options if compatible with Xpress problem control names self.prob_.setControl({ i: solver_opts[i] for i in solver_opts if hasattr(xp.controls.__dict__, i) }) if 'bargaptarget' not in solver_opts.keys(): self.prob_.controls.bargaptarget = 1e-30 if 'feastol' not in solver_opts.keys(): self.prob_.controls.feastol = 1e-9 # Solve problem results_dict = {"model": self.prob_} try: # If option given, write file before solving if 'write_mps' in solver_opts.keys(): self.prob_.write(solver_opts['write_mps']) self.prob_.solve() results_dict["cputime"] = self.prob_.attributes.time except xp.SolverError: # Error in the solution results_dict["status"] = s.SOLVER_ERROR else: results_dict['status'] = self.prob_.getProbStatus() results_dict[ 'getProbStatusString'] = self.prob_.getProbStatusString() results_dict['obj_value'] = self.prob_.getObjVal() try: results_dict[s.PRIMAL] = np.array(self.prob_.getSolution()) except xp.SolverError: results_dict[s.PRIMAL] = np.zeros(self.prob_.attributes.ncol) status_map_lp, status_map_mip = get_status_maps() if results_dict['status'] == 'solver_error': status = 'solver_error' elif 'mip_' in results_dict['getProbStatusString']: status = status_map_mip[results_dict['status']] else: status = status_map_lp[results_dict['status']] results_dict['bariter'] = self.prob_.attributes.bariter results_dict[ 'getProbStatusString'] = self.prob_.getProbStatusString() if status in s.SOLUTION_PRESENT: results_dict['getObjVal'] = self.prob_.getObjVal() results_dict['getSolution'] = self.prob_.getSolution() if not (data[s.BOOL_IDX] or data[s.INT_IDX]): results_dict['getDual'] = self.prob_.getDual() del self.prob_ return results_dict