def _construct_chain(self, solver=None, gp=False, enforce_dpp=False): """ Construct the chains required to reformulate and solve the problem. In particular, this function # finds the candidate solvers # constructs the solving chain that performs the numeric reductions and solves the problem. Arguments --------- solver : str, optional The solver to use. Defaults to ECOS. gp : bool, optional If True, the problem is parsed as a Disciplined Geometric Program instead of as a Disciplined Convex Program. enforce_dpp : bool, optional Whether to error on DPP violations. Returns ------- A solving chain """ candidate_solvers = self._find_candidate_solvers(solver=solver, gp=gp) return construct_solving_chain(self, candidate_solvers, gp=gp, enforce_dpp=enforce_dpp)
def check_solver(prob, solver_name): """Can the solver solve the problem? """ try: if solver_name == ROBUST_CVXOPT: solver_name = CVXOPT chain = construct_solving_chain(prob, solver=solver_name) return True except SolverError: return False except: raise
def cvxpy_solve(cvxpy_problem, iters=2, lsqr_iters=30, presolve=False, scs_opts={}, verbose=True, warm_start=True): from cvxpy.reductions.solvers.solving_chain import construct_solving_chain solving_chain = construct_solving_chain(cvxpy_problem, solver='SCS') data, inverse_data = solving_chain.apply(cvxpy_problem) start = time.time() if presolve: scs_solution = solving_chain.solve_via_data(cvxpy_problem, data=data, warm_start=warm_start, verbose=verbose, solver_opts=scs_opts) A, b, c, z, dims = cvxpy_scs_to_cpr(data, scs_solution) else: A, b, c, z, dims = cvxpy_scs_to_cpr(data) scs_solution = {} if warm_start and 'CPR' in cvxpy_problem._solver_cache: z = cvxpy_problem._solver_cache['CPR']['z'] prepare_time = time.time() - start # TODO change this start = time.time() refined_z = refine(A, b, c, dims, z, iters=iters, lsqr_iters=lsqr_iters, verbose=verbose) cvxpy_problem._solver_cache['CPR'] = {'z': refined_z} refine_time = time.time() - start new_residual, u, v = residual_and_uv( refined_z, (A.indptr, A.indices, A.data), b, c, make_prod_cone_cache(dims)) scs_solution['x'], scs_solution['s'], scs_solution[ 'y'], tau, kappa = uv2xsytaukappa(u, v, A.shape[1]) scs_solution["info"] = {'status': 'Solved', 'solveTime': refine_time, 'setupTime': prepare_time, 'iter': iters, 'pobj': scs_solution['x'] @ c if tau > 0 else np.nan} cvxpy_problem.unpack_results(scs_solution, solving_chain, inverse_data) return cvxpy_problem.value
def get_problem_data(self, solver): """Returns the problem data used in the call to the solver. When a problem is solved, a chain of reductions, called a :class:`~cvxpy.reductions.solvers.solving_chain.SolvingChain`, compiles it to some low-level representation that is compatible with the targeted solver. This method returns that low-level representation. For some solving chains, this low-level representation is a dictionary that contains exactly those arguments that were supplied to the solver; however, for other solving chains, the data is an intermediate representation that is compiled even further by libraries other than CVXPY. A solution to the equivalent low-level problem can be obtained via the data by invoking the solve_via_data method of the returned solving chain, a thin wrapper around the code external to CVXPY that further processes and solves the problem. Invoke the unpack_results method to recover a solution to the original problem. Parameters ---------- solver : str The solver the problem data is for. Returns ------- dict or object lowest level representation of problem SolvingChain The solving chain that created the data. list The inverse data generated by the chain. """ try: solving_chain = construct_solving_chain(self, solver) except Exception as e: raise e data, inv_data = solving_chain.apply(self) return data, solving_chain, inv_data
def _construct_chains(self, solver=None, gp=False): """ Construct the chains required to reformulate and solve the problem. In particular, this function #. finds the candidate solvers #. constructs the intermediate chain suitable for numeric reductions. #. constructs the solving chain that performs the numeric reductions and solves the problem. Parameters ---------- solver : str, optional The solver to use. Defaults to ECOS. gp : bool, optional If True, the problem is parsed as a Disciplined Geometric Program instead of as a Disciplined Convex Program. """ chain_key = (solver, gp) if chain_key != self._cached_chain_key: try: candidate_solvers = self._find_candidate_solvers(solver=solver, gp=gp) self._intermediate_chain = \ construct_intermediate_chain(self, candidate_solvers, gp=gp) self._intermediate_problem, self._intermediate_inverse_data = \ self._intermediate_chain.apply(self) self._solving_chain = \ construct_solving_chain(self._intermediate_problem, candidate_solvers) self._cached_chain_key = chain_key except Exception as e: raise e
def _solve(self, solver=None, ignore_dcp=False, warm_start=True, verbose=False, parallel=False, gp=False, **kwargs): """Solves a DCP compliant optimization problem. Saves the values of primal and dual variables in the variable and constraint objects, respectively. Parameters ---------- solver : str, optional The solver to use. Defaults to ECOS. ignore_dcp : bool, optional TODO(akshayka): This option will probably be eliminated. Overrides the default of raising an exception if the problem is not DCP. warm_start : bool, optional Should the previous solver result be used to warm start? verbose : bool, optional Overrides the default of hiding solver output. parallel : bool, optional If problem is separable, solve in parallel. gp : bool, optional If True, then parses the problem as a disciplined geometric program instead of a disciplined convex program. kwargs : dict, optional A dict of options that will be passed to the specific solver. In general, these options will override any default settings imposed by cvxpy. Returns ------- float The optimal value for the problem, or a string indicating why the problem could not be solved. """ if parallel: from cvxpy.transforms.separable_problems import get_separable_problems self._separable_problems = (get_separable_problems(self)) if len(self._separable_problems) > 1: return self._parallel_solve(solver, ignore_dcp, warm_start, verbose, **kwargs) chain_key = (solver, gp) if chain_key != self._cached_chain_key: try: self._solving_chain = construct_solving_chain(self, solver=solver, gp=gp) except Exception as e: raise e self._cached_chain_key = chain_key data, inverse_data = self._solving_chain.apply(self) solver_output = self._solving_chain.solve_via_data( self, data, warm_start, verbose, kwargs) self.unpack_results(solver_output, self._solving_chain, inverse_data) return self.value
def _solve(self, solver=None, ignore_dcp=False, warm_start=True, verbose=False, parallel=False, **kwargs): """Solves a DCP compliant optimization problem. Saves the values of primal and dual variables in the variable and constraint objects, respectively. Parameters ---------- solver : str, optional The solver to use. Defaults to ECOS. ignore_dcp : bool, optional TODO(akshayka): This option will probably be eliminated. Overrides the default of raising an exception if the problem is not DCP. warm_start : bool, optional Should the previous solver result be used to warm start? verbose : bool, optional Overrides the default of hiding solver output. parallel : bool, optional If problem is separable, solve in parallel. kwargs : dict, optional A dict of options that will be passed to the specific solver. In general, these options will override any default settings imposed by cvxpy. Returns ------- float The optimal value for the problem, or a string indicating why the problem could not be solved. """ if parallel: from cvxpy.transforms.separable_problems import get_separable_problems self._separable_problems = (get_separable_problems(self)) if len(self._separable_problems) > 1: return self._parallel_solve(solver, ignore_dcp, warm_start, verbose, **kwargs) # If a previous chain does not exist, or if the solver is # specified and it does not match the solver used for the previous # solve, then construct a new solving chain. if (self._solving_chain is None or (solver is not None and self._solving_chain.solver.name != solver)): try: self._solving_chain = construct_solving_chain(self, solver=solver) except Exception as e: raise e data, inverse_data = self._solving_chain.apply(self) solution = self._solving_chain.solve_via_data(self, data, warm_start, verbose, kwargs) self.unpack_results(solution, self._solving_chain, inverse_data) return self.value
def cvxpy_differentiate(cvxpy_problem, parameters, output_expression, iters=2, lsqr_iters=30, derive_lsqr_iters=100, presolve=False, scs_opts={}, verbose=True, warm_start=True): """Compute the derivative matrix of the solution map of a CVXPY problem, whose input is a list of CVXPY parameters, and output is a CVXPY one-dimensional expression of the solution, the constraint violations, or the dual parameters. Only affine CVXPY transformations are allowed. """ solving_chain = construct_solving_chain(cvxpy_problem, solver='SCS') data, inverse_data = solving_chain.apply(cvxpy_problem) A, b, c, _, _ = cvxpy_scs_to_cpr(data) # A is a sparse matrix, so below we compute # sparse matrix differences. input_mappings = [] # make mapping from input parameters to data for parameter in parameters: if verbose: print('compiling parameter', parameter.name()) old_par_val = parameter.value parameter.value += 1. newdata, _ = solving_chain.apply(cvxpy_problem) new_A, new_b, new_c, _, _ = cvxpy_scs_to_cpr(newdata) dA = new_A - A db = new_b - b dc = new_c - c parameter.value -= 2. newdata, _ = solving_chain.apply(cvxpy_problem) new_A, new_b, new_c, _, _ = cvxpy_scs_to_cpr(newdata) if not (np.allclose(A - new_A, dA)) \ and (np.allclose(b - new_b, db))\ and (np.allclose(c - new_c, dc)): raise NotAffine('on parameter %s' % parameter.name()) parameter.value = old_par_val input_mappings.append((dA, db, dc)) _ = cvxpy_solve(cvxpy_problem, iters=iters, lsqr_iters=lsqr_iters, presolve=presolve, scs_opts=scs_opts, verbose=verbose, warm_start=warm_start) # used by cvxpy to transform back scs_solution = {} scs_solution["info"] = {'status': 'Solved', 'solveTime': 0., 'setupTime': 0., 'iter': 0, 'pobj': np.nan} z = cvxpy_problem._solver_cache['CPR']['z'] if not (len(output_expression.shape) == 1): raise ValueError('Only one-dimensional outputs') output_matrix = np.empty((len(z), output_expression.size)) base = output_expression.value # make mapping from z to output for i in range(len(z)): # perturb z old_val = z[i] z[i] += old_val * 1e-8 _, new_u, new_v = residual_and_uv( z, (A.indptr, A.indices, A.data), b, c, make_prod_cone_cache(dims)) scs_solution['x'], scs_solution['s'], scs_solution[ 'y'], tau, kappa = uv2xsytaukappa(new_u, new_v, A.shape[1]) cvxpy_problem.unpack_results(scs_solution, solving_chain, inverse_data) output_matrix[i, :] = output_expression.value - base z[i] -= old_val * 2e-8 _, new_u, new_v = residual_and_uv( z, (A.indptr, A.indices, A.data), b, c, make_prod_cone_cache(dims)) scs_solution['x'], scs_solution['s'], scs_solution[ 'y'], tau, kappa = uv2xsytaukappa(new_u, new_v, A.shape[1]) cvxpy_problem.unpack_results(scs_solution, solving_chain, inverse_data) if not np.allclose(output_matrix[i, :], base - output_expression.value): raise NotAffine('on solution variable z[%d]' % i) z[i] = old_val def matvec(d_parameters): assert len(d_parameters) == len(parameters) total_dA = sp.csc_matrix() total_db = np.zeros(len(b)) total_dc = np.zeros(len(c)) for i in len(d_parameters): total_dA += input_mappings[i][0] * d_parameters[i] total_db += input_mappings[i][1] * d_parameters[i] total_dc += input_mappings[i][2] * d_parameters[i] refined_z = refine(A + total_dA, b + total_db, c + total_dc, dims, z, iters=1, lsqr_iters=derive_lsqr_iters, verbose=verbose) dz = refined_z - z return dz @ output_matrix def rmatvec(d_output): dz = output_matrix @ d_output result = LinearOperator((len(parameters), output_expression.size), matvec=matvec, rmatvec=rmatvec, dtype=np.float)