def get_problem_data(self, objective, constraints, cached_data): """Returns the argument for the call to the solver. Parameters ---------- objective : LinOp The canonicalized objective. constraints : list The list of canonicalized cosntraints. cached_data : dict A map of solver name to cached problem data. Returns ------- dict The arguments needed for the solver. """ # Returns CVXOPT matrices so can be used by raw CVXOPT solver. data = super(CVXOPT, self).get_problem_data(objective, constraints, cached_data) # Convert A, b, G, h, c to CVXOPT matrices. data[s.A] = intf.sparse2cvxopt(data[s.A]) data[s.G] = intf.sparse2cvxopt(data[s.G]) data[s.B] = intf.dense2cvxopt(data[s.B]) data[s.H] = intf.dense2cvxopt(data[s.H]) data[s.C] = intf.dense2cvxopt(data[s.C]) return data
def solve(self, objective, constraints, cached_data, warm_start, verbose, solver_opts): """Returns the result of the call to the solver. Parameters ---------- objective : LinOp The canonicalized objective. constraints : list The list of canonicalized cosntraints. cached_data : dict A map of solver name to cached problem data. warm_start : bool Not used. verbose : bool Should the solver print output? solver_opts : dict Additional arguments for the solver. Returns ------- tuple (status, optimal value, primal, equality dual, inequality dual) """ import cvxopt import cvxopt.solvers data = super(CVXOPT, self).get_problem_data(objective, constraints, cached_data) # Save old data in case need to use robust solver. data[s.DIMS] = copy.deepcopy(data[s.DIMS]) # Convert all longs to ints. for key, val in list(data[s.DIMS].items()): if isinstance(val, list): data[s.DIMS][key] = [int(v) for v in val] else: data[s.DIMS][key] = int(val) # User chosen KKT solver option. kktsolver = self.get_kktsolver_opt(solver_opts) # Cannot have redundant rows unless using robust LDL kktsolver. if kktsolver != s.ROBUST_KKTSOLVER: # Will detect infeasibility. if self.remove_redundant_rows(data) == s.INFEASIBLE: return {s.STATUS: s.INFEASIBLE} # Convert A, b, G, h, c to CVXOPT matrices. data[s.A] = intf.sparse2cvxopt(data[s.A]) data[s.G] = intf.sparse2cvxopt(data[s.G]) data[s.B] = intf.dense2cvxopt(data[s.B]) data[s.H] = intf.dense2cvxopt(data[s.H]) data[s.C] = intf.dense2cvxopt(data[s.C]) # Save original cvxopt solver options. old_options = cvxopt.solvers.options.copy() # Silence cvxopt if verbose is False. cvxopt.solvers.options["show_progress"] = verbose # Apply any user-specific options. # Rename max_iters to maxiters. if "max_iters" in solver_opts: solver_opts["maxiters"] = solver_opts["max_iters"] for key, value in list(solver_opts.items()): cvxopt.solvers.options[key] = value # Always do 1 step of iterative refinement after solving KKT system. if "refinement" not in cvxopt.solvers.options: cvxopt.solvers.options["refinement"] = 1 try: # Target cvxopt clp if nonlinear constraints exist. if data[s.DIMS][s.EXP_DIM]: results_dict = self.cpl_solve(data, kktsolver) else: results_dict = self.conelp_solve(data, kktsolver) # Catch exceptions in CVXOPT and convert them to solver errors. except ValueError: results_dict = {"status": "unknown"} # Restore original cvxopt solver options. self._restore_solver_options(old_options) return self.format_results(results_dict, data, cached_data)
def remove_redundant_rows(data): """Check if A has redundant rows. If it does, remove redundant constraints from A, and apply a presolve procedure for G. Parameters ---------- data : dict All the problem data. Returns ------- str A status indicating if infeasibility was detected. """ # Extract data. dims = data[s.DIMS] A = data[s.A] G = data[s.G] b = data[s.B] h = data[s.H] if A is None: return s.OPTIMAL TOL = 1e-10 # # Use a gram matrix approach to skip dense QR factorization, if possible. # gram = A @ A.T if gram.shape[0] == 1: gram = gram.toarray().item() # we only have one equality constraint. if gram > 0: return s.OPTIMAL elif not b.item() == 0.0: return s.INFEASIBLE else: data[s.A] = None data[s.B] = None return s.OPTIMAL eig = extremal_eig_near_ref(gram, ref=TOL) if eig > TOL: return s.OPTIMAL # # Redundant constraints exist, up to numerical tolerance; # reformulate equality constraints to remove this redundancy. # Q, R, P = scipy.linalg.qr(A.todense(), pivoting=True) # pivoting helps robustness rows_to_keep = [] for i in range(R.shape[0]): if np.linalg.norm(R[i, :]) > TOL: rows_to_keep.append(i) R = R[rows_to_keep, :] Q = Q[:, rows_to_keep] # Invert P from col -> var to var -> col. Pinv = np.zeros(P.size, dtype='int') for i in range(P.size): Pinv[P[i]] = i # Rearrage R. R = R[:, Pinv] A = R b_old = b b = Q.T.dot(b) # If b is not in the range of Q, the problem is infeasible. if not np.allclose(b_old, Q.dot(b)): return s.INFEASIBLE dims[s.EQ_DIM] = int(b.shape[0]) data["Q"] = intf.dense2cvxopt(Q) # # Since we're applying nontrivial presolve to A, apply to G as well. # if G is not None: G = G.tocsr() G_leq = G[:dims[s.LEQ_DIM], :] h_leq = h[:dims[s.LEQ_DIM]].ravel() G_other = G[dims[s.LEQ_DIM]:, :] h_other = h[dims[s.LEQ_DIM]:].ravel() G_leq, h_leq, P_leq = compress_matrix(G_leq, h_leq) dims[s.LEQ_DIM] = int(h_leq.shape[0]) data["P_leq"] = intf.sparse2cvxopt(P_leq) G = sp.vstack([G_leq, G_other]) h = np.hstack([h_leq, h_other]) # Record changes, and return. data[s.A] = A data[s.G] = G data[s.B] = b data[s.H] = h return s.OPTIMAL
def solve_via_data(self, data, warm_start, verbose, solver_opts, solver_cache=None): import cvxopt.solvers # Save original cvxopt solver options. old_options = cvxopt.solvers.options.copy() # Save old data in case need to use robust solver. data[s.DIMS] = dims_to_solver_dict(data[s.DIMS]) # Do a preliminary check for a certain, problematic KKT solver. kktsolver = self.get_kktsolver_opt(solver_opts) if isinstance(kktsolver, str) and kktsolver == 'chol': if self.remove_redundant_rows(data) == s.INFEASIBLE: return {s.STATUS: s.INFEASIBLE} # Convert A, b, G, h, c to CVXOPT matrices. data[s.C] = intf.dense2cvxopt(data[s.C]) var_length = data[s.C].size[0] if data[s.A] is None: data[s.A] = np.zeros((0, var_length)) data[s.B] = np.zeros((0, 1)) data[s.A] = intf.sparse2cvxopt(data[s.A]) data[s.B] = intf.dense2cvxopt(data[s.B]) if data[s.G] is None: data[s.G] = np.zeros((0, var_length)) data[s.H] = np.zeros((0, 1)) data[s.G] = intf.sparse2cvxopt(data[s.G]) data[s.H] = intf.dense2cvxopt(data[s.H]) c, G, h, dims = data[s.C], data[s.G], data[s.H], data[s.DIMS] A, b = data[s.A], data[s.B] # Apply any user-specific options. # Silence solver. solver_opts["show_progress"] = verbose # Rename max_iters to maxiters. if "max_iters" in solver_opts: solver_opts["maxiters"] = solver_opts["max_iters"] for key, value in solver_opts.items(): cvxopt.solvers.options[key] = value # Always do 1 step of iterative refinement after solving KKT system. if "refinement" not in cvxopt.solvers.options: cvxopt.solvers.options["refinement"] = 1 # finalize the KKT solver. if isinstance(kktsolver, str) and kktsolver == s.ROBUST_KKTSOLVER: kktsolver = setup_ldl_factor(c, G, h, dims, A, b) elif not isinstance(kktsolver, str): kktsolver = kktsolver(c, G, h, dims, A, b) try: results_dict = cvxopt.solvers.conelp(c, G, h, dims, A, b, kktsolver=kktsolver) # Catch exceptions in CVXOPT and convert them to solver errors. except ValueError: results_dict = {"status": "unknown"} # Restore original cvxopt solver options. self._restore_solver_options(old_options) # Construct solution. solution = {} status = self.STATUS_MAP[results_dict['status']] solution[s.STATUS] = status if solution[s.STATUS] in s.SOLUTION_PRESENT: primal_val = results_dict['primal objective'] solution[s.VALUE] = primal_val solution[s.PRIMAL] = results_dict['x'] solution[s.EQ_DUAL] = results_dict['y'] solution[s.INEQ_DUAL] = results_dict['z'] # Need to multiply duals by Q and P_leq. if "Q" in data: y = results_dict['y'] # Test if all constraints eliminated. if y.size[0] == 0: dual_len = data["Q"].size[0] solution[s.EQ_DUAL] = cvxopt.matrix(0., (dual_len, 1)) else: solution[s.EQ_DUAL] = data["Q"]*y if "P_leq" in data: leq_len = data[s.DIMS][s.LEQ_DIM] P_rows = data["P_leq"].size[0] new_len = P_rows + solution[s.INEQ_DUAL].size[0] - leq_len new_dual = cvxopt.matrix(0., (new_len, 1)) z = solution[s.INEQ_DUAL][:leq_len] # Test if all constraints eliminated. if z.size[0] == 0: new_dual[:P_rows] = 0 else: new_dual[:P_rows] = data["P_leq"] * z new_dual[P_rows:] = solution[s.INEQ_DUAL][leq_len:] solution[s.INEQ_DUAL] = new_dual for key in [s.PRIMAL, s.EQ_DUAL, s.INEQ_DUAL]: solution[key] = intf.cvxopt2dense(solution[key]) return solution
def remove_redundant_rows(data): """Remove redundant constraints from A and G. Parameters ---------- data : dict All the problem data. Returns ------- str A status indicating if infeasibility was detected. """ # Extract data. dims = data[s.DIMS] A = data[s.A] G = data[s.G] b = data[s.B] h = data[s.H] # Remove redundant rows in A. if A is not None: # The pivoting improves robustness. Q, R, P = scipy.linalg.qr(A.todense(), pivoting=True) rows_to_keep = [] for i in range(R.shape[0]): if np.linalg.norm(R[i, :]) > 1e-10: rows_to_keep.append(i) R = R[rows_to_keep, :] Q = Q[:, rows_to_keep] # Invert P from col -> var to var -> col. Pinv = np.zeros(P.size, dtype='int') for i in range(P.size): Pinv[P[i]] = i # Rearrage R. R = R[:, Pinv] A = R b_old = b b = Q.T.dot(b) # If b is not in the range of Q, # the problem is infeasible. if not np.allclose(b_old, Q.dot(b)): return s.INFEASIBLE dims[s.EQ_DIM] = int(b.shape[0]) data["Q"] = intf.dense2cvxopt(Q) # Remove obviously redundant rows in G's <= constraints. if G is not None: G = G.tocsr() G_leq = G[:dims[s.LEQ_DIM], :] h_leq = h[:dims[s.LEQ_DIM]].ravel() G_other = G[dims[s.LEQ_DIM]:, :] h_other = h[dims[s.LEQ_DIM]:].ravel() G_leq, h_leq, P_leq = compress_matrix(G_leq, h_leq) dims[s.LEQ_DIM] = int(h_leq.shape[0]) data["P_leq"] = intf.sparse2cvxopt(P_leq) G = sp.vstack([G_leq, G_other]) h = np.hstack([h_leq, h_other]) # Convert A, b, G, h to CVXOPT matrices. data[s.A] = A data[s.G] = G data[s.B] = b data[s.H] = h return s.OPTIMAL
def remove_redundant_rows(data): """Remove redundant constraints from A and G. Parameters ---------- data : dict All the problem data. Returns ------- str A status indicating if infeasibility was detected. """ # Extract data. dims = data[s.DIMS] A = data[s.A] G = data[s.G] b = data[s.B] h = data[s.H] # Remove redundant rows in A. if A.shape[0] > 0: # The pivoting improves robustness. Q, R, P = scipy.linalg.qr(A.todense(), pivoting=True) rows_to_keep = [] for i in range(R.shape[0]): if np.linalg.norm(R[i, :]) > 1e-10: rows_to_keep.append(i) R = R[rows_to_keep, :] Q = Q[:, rows_to_keep] # Invert P from col -> var to var -> col. Pinv = np.zeros(P.size, dtype='int') for i in range(P.size): Pinv[P[i]] = i # Rearrage R. R = R[:, Pinv] A = R b_old = b b = Q.T.dot(b) # If b is not in the range of Q, # the problem is infeasible. if not np.allclose(b_old, Q.dot(b)): return s.INFEASIBLE dims[s.EQ_DIM] = int(b.shape[0]) data["Q"] = intf.dense2cvxopt(Q) # Remove obviously redundant rows in G's <= constraints. if dims[s.LEQ_DIM] > 0: G = G.tocsr() G_leq = G[:dims[s.LEQ_DIM], :] h_leq = h[:dims[s.LEQ_DIM]].ravel() G_other = G[dims[s.LEQ_DIM]:, :] h_other = h[dims[s.LEQ_DIM]:].ravel() G_leq, h_leq, P_leq = compress_matrix(G_leq, h_leq) dims[s.LEQ_DIM] = int(h_leq.shape[0]) data["P_leq"] = intf.sparse2cvxopt(P_leq) G = sp.vstack([G_leq, G_other]) h = np.hstack([h_leq, h_other]) # Convert A, b, G, h to CVXOPT matrices. data[s.A] = A data[s.G] = G data[s.B] = b data[s.H] = h return s.OPTIMAL
def solve(self, objective, constraints, cached_data, warm_start, verbose, solver_opts): """Returns the result of the call to the solver. Parameters ---------- objective : LinOp The canonicalized objective. constraints : list The list of canonicalized cosntraints. cached_data : dict A map of solver name to cached problem data. warm_start : bool Not used. verbose : bool Should the solver print output? solver_opts : dict Additional arguments for the solver. Returns ------- tuple (status, optimal value, primal, equality dual, inequality dual) """ import cvxopt import cvxopt.solvers data = super(CVXOPT, self).get_problem_data(objective, constraints, cached_data) # Save old data in case need to use robust solver. data[s.DIMS] = copy.deepcopy(data[s.DIMS]) # Convert all longs to ints. for key, val in data[s.DIMS].items(): if isinstance(val, list): data[s.DIMS][key] = [int(v) for v in val] else: data[s.DIMS][key] = int(val) # User chosen KKT solver option. kktsolver = self.get_kktsolver_opt(solver_opts) # Cannot have redundant rows unless using robust LDL kktsolver. if kktsolver != s.ROBUST_KKTSOLVER: # Will detect infeasibility. if self.remove_redundant_rows(data) == s.INFEASIBLE: return {s.STATUS: s.INFEASIBLE} # Convert A, b, G, h, c to CVXOPT matrices. data[s.A] = intf.sparse2cvxopt(data[s.A]) data[s.G] = intf.sparse2cvxopt(data[s.G]) data[s.B] = intf.dense2cvxopt(data[s.B]) data[s.H] = intf.dense2cvxopt(data[s.H]) data[s.C] = intf.dense2cvxopt(data[s.C]) # Save original cvxopt solver options. old_options = cvxopt.solvers.options.copy() # Silence cvxopt if verbose is False. cvxopt.solvers.options["show_progress"] = verbose # Apply any user-specific options. # Rename max_iters to maxiters. if "max_iters" in solver_opts: solver_opts["maxiters"] = solver_opts["max_iters"] for key, value in solver_opts.items(): cvxopt.solvers.options[key] = value # Always do 1 step of iterative refinement after solving KKT system. if "refinement" not in cvxopt.solvers.options: cvxopt.solvers.options["refinement"] = 1 try: # Target cvxopt clp if nonlinear constraints exist. if data[s.DIMS][s.EXP_DIM]: results_dict = self.cpl_solve(data, kktsolver) else: results_dict = self.conelp_solve(data, kktsolver) # Catch exceptions in CVXOPT and convert them to solver errors. except ValueError: results_dict = {"status": "unknown"} # Restore original cvxopt solver options. self._restore_solver_options(old_options) return self.format_results(results_dict, data, cached_data)