예제 #1
0
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
예제 #2
0
 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)
예제 #3
0
 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
예제 #4
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 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
예제 #5
0
    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)
예제 #6
0
 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
예제 #7
0
    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
예제 #8
0
 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'))
예제 #9
0
 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)))
예제 #10
0
    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'))
예제 #11
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