Skip to content

Commit

Permalink
Cherry-pick cvxpy#2080
Browse files Browse the repository at this point in the history
  • Loading branch information
allenlawrence94 authored and Paulnkk committed Jul 15, 2023
1 parent 3d29dd5 commit 67c80f4
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 18 deletions.
9 changes: 7 additions & 2 deletions cvxpy/reductions/solvers/conic_solvers/scipy_conif.py
Expand Up @@ -38,7 +38,7 @@ class SCIPY(ConicSolver):

# Map of SciPy linprog status
STATUS_MAP = {0: s.OPTIMAL, # Optimal
1: s.SOLVER_ERROR, # Iteration limit reached
1: s.OPTIMAL_INACCURATE, # Iteration/time limit reached
2: s.INFEASIBLE, # Infeasible
3: s.UNBOUNDED, # Unbounded
4: s.SOLVER_ERROR # Numerical difficulties encountered
Expand Down Expand Up @@ -171,7 +171,12 @@ def solve_via_data(self, data, warm_start: bool, verbose: bool, solver_opts, sol
def invert(self, solution, inverse_data):
"""Returns the solution to the original problem given the inverse_data.
"""
status = self.STATUS_MAP[solution['status']]
status = self.STATUS_MAP[solution["status"]]

# Sometimes when the solver's time limit is reached, the solver doesn't return a solution.
# In these situations we correct the status from s.OPTIMAL_INACCURATE to s.SOLVER_ERROR
if (status == s.OPTIMAL_INACCURATE) and (solution.x is None):
status = s.SOLVER_ERROR

primal_vars = None
dual_vars = None
Expand Down
30 changes: 14 additions & 16 deletions cvxpy/tests/test_conic_solvers.py
Expand Up @@ -24,6 +24,7 @@

import cvxpy as cp
import cvxpy.tests.solver_test_helpers as sths
from cvxpy import SolverError
from cvxpy.reductions.solvers.defines import (
INSTALLED_MI_SOLVERS,
INSTALLED_SOLVERS,
Expand Down Expand Up @@ -1561,22 +1562,6 @@ def test_scip_test_params__invalid_scip_params(self) -> None:
exc = "One or more scip params in ['a'] are not valid: 'Not a valid parameter name'"
assert ke.exception == exc

def test_scip_time_limit_reached(self) -> None:
sth = sths.mi_lp_7()

# TODO doesn't work on windows.
# run without enough time to find optimum
# sth.solve(solver="SCIP", scip_params={"limits/time": 0.01})
# assert sth.prob.status == cp.OPTIMAL_INACCURATE
# assert all([v.value is not None for v in sth.prob.variables()])

# run without enough time to do anything
with pytest.raises(cp.error.SolverError) as se:
sth.solve(solver="SCIP", scip_params={"limits/time": 0.0})
exc = "Solver 'SCIP' failed. " \
"Try another solver, or solve with verbose=True for more information."
assert str(se.value) == exc


class TestAllSolvers(BaseTest):

Expand Down Expand Up @@ -1730,3 +1715,16 @@ def test_scipy_lp_4(self) -> None:

def test_scipy_lp_5(self) -> None:
StandardTestLPs.test_lp_5(solver='SCIPY', duals=self.d)

@unittest.skipUnless('SCIPY' in INSTALLED_MI_SOLVERS, 'SCIPY version cannot solve MILPs')
def test_scipy_mi_time_limit_reached(self) -> None:
sth = sths.mi_lp_7()

# run without enough time to find optimum
sth.solve(solver='SCIPY', scipy_options={"time_limit": 0.01})
assert sth.prob.status == cp.OPTIMAL_INACCURATE
assert sth.objective.value > 0

# run without enough time to do anything
with pytest.raises(SolverError):
sth.solve(solver='SCIPY', scipy_options={"time_limit": 0.})

0 comments on commit 67c80f4

Please sign in to comment.