def dims_to_solver_dict(cone_dims): cones = dims_to_solver_dict_default(cone_dims) import scs if Version(scs.__version__) >= Version('3.0.0'): cones['z'] = cones.pop('f') # renamed to 'z' in SCS 3.0.0 return cones
def import_solver(self) -> None: """Imports the solver.""" import pyscipopt v = pyscipopt.__version__ if Version(v) >= Version('4.0.0'): msg = 'PySCIPOpt (SCIP\'s Python wrapper) is installed and its' \ 'version is %s. CVXPY only supports PySCIPOpt < 4.0.0.' % v raise NotImplementedError(msg)
def import_solver(self) -> None: """Imports the solver.""" import google.protobuf import ortools if Version(ortools.__version__) < Version('9.3.0'): raise RuntimeError(f'Version of ortools ({ortools.__version__}) ' f'is too old. Expected >= 9.3.0.') ortools, google.protobuf # For flake8
def solve_via_data(self, data, warm_start: bool, verbose: bool, solver_opts, solver_cache=None): """Returns the result of the call to the solver. Parameters ---------- data : dict Data generated via an apply call. warm_start : Bool Whether to warm_start diffcp. verbose : Bool Control the verbosity. solver_opts : dict SCS-specific solver options. Returns ------- The result returned by a call to scs.solve(). """ import diffcp A = data[s.A] b = data[s.B] c = data[s.C] cones = scs_conif.dims_to_solver_dict(data[ConicSolver.DIMS]) solver_opts["solve_method"] = solver_opts.get("solve_method", s.SCS) warm_start_tuple = None if solver_opts["solve_method"] == s.SCS: import scs if Version(scs.__version__) < Version('3.0.0'): # Default to eps = 1e-4 instead of 1e-3. solver_opts["eps"] = solver_opts.get("eps", 1e-4) else: solver_opts['eps_abs'] = solver_opts.get('eps_abs', 1e-5) solver_opts['eps_rel'] = solver_opts.get('eps_rel', 1e-5) if warm_start and solver_cache is not None and \ self.name() in solver_cache: warm_start_tuple = (solver_cache[self.name()]["x"], solver_cache[self.name()]["y"], solver_cache[self.name()]["s"]) start = time.time() results = diffcp.solve_and_derivative_internal( A, b, c, cones, verbose=verbose, warm_start=warm_start_tuple, raise_on_error=False, **solver_opts) end = time.time() results["TOT_TIME"] = end - start results["solve_method"] = solver_opts["solve_method"] if solver_cache is not None: solver_cache[self.name()] = results return results
def invert(self, solution, inverse_data): """Returns the solution to the original problem given the inverse_data. """ attr = {} if solution["solve_method"] == s.SCS: import scs if Version(scs.__version__) < Version('3.0.0'): status = scs_conif.SCS.STATUS_MAP[solution["info"]["statusVal"]] attr[s.SOLVE_TIME] = solution["info"]["solveTime"] attr[s.SETUP_TIME] = solution["info"]["setupTime"] else: status = scs_conif.SCS.STATUS_MAP[solution["info"]["status_val"]] attr[s.SOLVE_TIME] = solution["info"]["solve_time"] attr[s.SETUP_TIME] = solution["info"]["setup_time"] elif solution["solve_method"] == s.ECOS: status = self.STATUS_MAP[solution["info"]["status"]] attr[s.SOLVE_TIME] = solution["info"]["solveTime"] attr[s.SETUP_TIME] = solution["info"]["setupTime"] attr[s.NUM_ITERS] = solution["info"]["iter"] attr[s.EXTRA_STATS] = solution if status in s.SOLUTION_PRESENT: primal_val = solution["info"]["pobj"] opt_val = primal_val + inverse_data[s.OFFSET] # TODO expand primal and dual variables from lower triangular to full. # TODO but this makes map from solution to variables not a slice. primal_vars = { inverse_data[DIFFCP.VAR_ID]: solution["x"] } eq_dual_vars = utilities.get_dual_values( solution["y"][:inverse_data[ConicSolver.DIMS].zero], self.extract_dual_value, inverse_data[DIFFCP.EQ_CONSTR] ) ineq_dual_vars = utilities.get_dual_values( solution["y"][inverse_data[ConicSolver.DIMS].zero:], self.extract_dual_value, inverse_data[DIFFCP.NEQ_CONSTR] ) dual_vars = {} dual_vars.update(eq_dual_vars) dual_vars.update(ineq_dual_vars) return Solution(status, opt_val, primal_vars, dual_vars, attr) else: return failure_solution(status, attr)
def parse_solver_options(solver_opts): import scs if Version(scs.__version__) < Version('3.0.0'): if "eps_abs" in solver_opts or "eps_rel" in solver_opts: # Take the min of eps_rel and eps_abs to be eps solver_opts["eps"] = min(solver_opts.get("eps_abs", 1), solver_opts.get("eps_rel", 1)) else: # Default to eps = 1e-4 instead of 1e-3. solver_opts["eps"] = solver_opts.get("eps", 1e-4) else: if "eps" in solver_opts: # eps replaced by eps_abs, eps_rel solver_opts["eps_abs"] = solver_opts["eps"] solver_opts["eps_rel"] = solver_opts["eps"] del solver_opts["eps"] else: solver_opts['eps_abs'] = solver_opts.get('eps_abs', 1e-5) solver_opts['eps_rel'] = solver_opts.get('eps_rel', 1e-5) return solver_opts
def solve_via_data(self, data, warm_start: bool, verbose: bool, solver_opts, solver_cache=None): from scipy import optimize as opt # Set default method which can be overriden by user inputs if (Version(scipy.__version__) < Version('1.6.1')): meth = "interior-point" else: meth = "highs" # Extract solver options which are not part of the options dictionary if solver_opts: # Raise error message if the parameters are not passed in # a dictionary called 'scipy_options'. if "scipy_options" not in solver_opts: raise ValueError( "All parameters for the SCIPY solver should " "be incased within a dictionary called " "scipy_options e.g. \n" "prob.solve(solver='SCIPY', verbose=True," " scipy_options={'method':'highs-ds', 'maxiter':10000})") # Raise warning if the 'method' parameter is not specified if "method" not in solver_opts['scipy_options']: warnings.warn("It is best to specify the 'method' parameter " "within scipy_options. The main advantage " "of this solver, is its ability to use the " "HiGHS LP solvers via scipy.optimize.linprog() " "which require a SciPy version >= 1.6.1 ." "\n\nThe default method '{}' will be" " used in this case.\n".format(meth)) else: meth = solver_opts['scipy_options'].pop("method") # Check to see if scipy version larger than 1.6.1 is installed # if method chosen is one of the highs methods. ver = (Version(scipy.__version__) < Version('1.6.1')) if ((meth in ['highs-ds', 'highs-ipm', 'highs']) & ver): raise ValueError( "The HiGHS solvers require a SciPy version >= 1.6.1") # Disable the 'bounds' parameter to avoid problems with # canonicalised problems. if "bounds" in solver_opts['scipy_options']: raise ValueError("Please do not specify bounds through " "scipy_options. Please specify bounds " "through CVXPY.") # Not supported by HiGHS solvers: # callback = solver_opts['scipy_options'].pop("callback", None) # x0 = solver_opts['scipy_options'].pop("x0", None) # Run the optimisation using scipy.optimize.linprog solution = opt.linprog(data[s.C], A_ub=data[s.G], b_ub=data[s.H], A_eq=data[s.A], b_eq=data[s.B], method=meth, bounds=(None, None), options=solver_opts['scipy_options']) else: warnings.warn("It is best to specify the 'method' parameter " "within scipy_options. The main advantage " "of this solver, is its ability to use the " "HiGHS LP solvers via scipy.optimize.linprog() " "which require a SciPy version >= 1.6.1 ." "\n\nThe default method '{}' will be" " used in this case.\n".format(meth)) # Run the optimisation using scipy.optimize.linprog solution = opt.linprog(data[s.C], A_ub=data[s.G], b_ub=data[s.H], A_eq=data[s.A], b_eq=data[s.B], method=meth, bounds=(None, None)) if verbose is True: print("Solver terminated with message: " + solution.message) return solution
def test_local_version_identifiers(self): self.assertTrue(Version('1.0.0') == Version('1.0.0+1')) self.assertTrue(Version('1.0.0') == Version('1.0.0+xxx')) self.assertTrue(Version('1.0.0') == Version('1.0.0+x.y.z'))
def test_tuple_construction(self): self.assertTrue(Version('0100.2.03') == Version((100, 2, 3))) self.assertTrue(Version('1.2.3') == Version((1, 2, 3, None))) self.assertTrue(Version('1.2.3') == Version((1, 2, 3, 'junk'))) self.assertTrue(Version('1.2.3') == Version((1, 2, 3, -1)))
def test_typical_inputs(self): self.assertTrue(Version('1.0.0') < Version('2.0.0')) self.assertTrue(Version('1.0.0') < Version('1.1.0')) self.assertTrue(Version('1.0.0') < Version('1.0.1')) self.assertFalse(Version('1.0.0') < Version('1.0.0')) self.assertTrue(Version('1.0.0') <= Version('1.0.0')) self.assertFalse(Version('1.0.0') >= Version('2.0.0')) self.assertFalse(Version('1.0.0') >= Version('1.1.0')) self.assertFalse(Version('1.0.0') >= Version('1.0.1')) self.assertTrue(Version('1.0.0') >= Version('1.0.0')) self.assertFalse(Version('1.0.0') > Version('1.0.0'))
def solve_via_data(self, data, warm_start: bool, verbose: bool, solver_opts, solver_cache=None): """Returns the result of the call to the solver. Parameters ---------- data : dict Data generated via an apply call. warm_start : Bool Whether to warm_start SCS. verbose : Bool Control the verbosity. solver_opts : dict SCS-specific solver options. Returns ------- The result returned by a call to scs.solve(). """ import scs scs_version = Version(scs.__version__) args = {"A": data[s.A], "b": data[s.B], "c": data[s.C]} if warm_start and solver_cache is not None and \ self.name() in solver_cache: args["x"] = solver_cache[self.name()]["x"] args["y"] = solver_cache[self.name()]["y"] args["s"] = solver_cache[self.name()]["s"] cones = dims_to_solver_dict(data[ConicSolver.DIMS]) def solve(_solver_opts): if scs_version.major < 3: _results = scs.solve(args, cones, verbose=verbose, **_solver_opts) _status = self.STATUS_MAP[_results["info"]["statusVal"]] else: _results = scs.solve(args, cones, verbose=verbose, **_solver_opts) _status = self.STATUS_MAP[_results["info"]["status_val"]] return _results, _status solver_opts = SCS.parse_solver_options(solver_opts) results, status = solve(solver_opts) if (status in s.INACCURATE and scs_version.major == 2 and "acceleration_lookback" not in solver_opts): import warnings warnings.warn(SCS.ACCELERATION_RETRY_MESSAGE % str(scs_version)) retry_opts = solver_opts.copy() retry_opts["acceleration_lookback"] = 0 results, status = solve(retry_opts) if solver_cache is not None and status == s.OPTIMAL: solver_cache[self.name()] = results return results