Skip to content

Commit

Permalink
Basic solution in Mosek (cvxpy#347)
Browse files Browse the repository at this point in the history
* Added a selector between basic and interior point solution in Mosek

* Expanded a test to the case of only basic solution available
  • Loading branch information
aszekMosek authored and SteveDiamond committed Mar 29, 2017
1 parent 6f519f6 commit cde8afb
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 6 deletions.
58 changes: 52 additions & 6 deletions cvxpy/problems/solvers/mosek_intf.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,50 @@ def streamprinter(text):

return self.format_results(task, data, cached_data)

def choose_solution(self, task):
"""Chooses between the basic and interior point solution.
Parameters
----------
task : mosek.Task
The solver status interface.
Returns
-------
soltype
The preferred solution (mosek.soltype.*)
solsta
The status of the preferred solution (mosek.solsta.*)
"""
import mosek

def rank(status):
# Rank solutions
# optimal > near_optimal > anything else > None
if status == mosek.solsta.optimal:
return 3
elif status == mosek.solsta.near_optimal:
return 2
elif status is not None:
return 1
else:
return 0

solsta_bas, solsta_itr = None, None

if task.solutiondef(mosek.soltype.bas):
solsta_bas = task.getsolsta(mosek.soltype.bas)

if task.solutiondef(mosek.soltype.itr):
solsta_itr = task.getsolsta(mosek.soltype.itr)

# As long as interior solution is not worse, take it
# (for backward compatibility)
if rank(solsta_itr) >= rank(solsta_bas):
return mosek.soltype.itr, solsta_itr
else:
return mosek.soltype.bas, solsta_bas

def format_results(self, task, data, cached_data):
"""Converts the solver output into standard form.
Expand Down Expand Up @@ -273,10 +317,12 @@ def format_results(self, task, data, cached_data):
mosek.solsta.near_dual_infeas_cer: s.UNBOUNDED_INACCURATE,
mosek.solsta.unknown: s.SOLVER_ERROR}

task.getprosta(mosek.soltype.itr) # unused
solsta = task.getsolsta(mosek.soltype.itr)
soltype, solsta = self.choose_solution(task)

result_dict = {s.STATUS: STATUS_MAP[solsta]}
if solsta in STATUS_MAP:
result_dict = {s.STATUS: STATUS_MAP[solsta]}
else:
result_dict = {s.STATUS: s.SOLVER_ERROR}

# Callback data example:
# http://docs.mosek.com/7.1/pythonapi/The_progress_call-back.html
Expand All @@ -290,13 +336,13 @@ def format_results(self, task, data, cached_data):
if result_dict[s.STATUS] in s.SOLUTION_PRESENT:
# get primal variables values
result_dict[s.PRIMAL] = np.zeros(task.getnumvar(), dtype=np.float)
task.getxx(mosek.soltype.itr, result_dict[s.PRIMAL])
task.getxx(soltype, result_dict[s.PRIMAL])
# get obj value
result_dict[s.VALUE] = task.getprimalobj(mosek.soltype.itr) + \
result_dict[s.VALUE] = task.getprimalobj(soltype) + \
data[s.OFFSET]
# get dual
y = np.zeros(task.getnumcon(), dtype=np.float)
task.gety(mosek.soltype.itr, y)
task.gety(soltype, y)
# it appears signs are inverted
result_dict[s.EQ_DUAL] = -y[:len(data[s.B])]
result_dict[s.INEQ_DUAL] = \
Expand Down
6 changes: 6 additions & 0 deletions cvxpy/tests/test_solvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,12 @@ def test_mosek(self):
prob.solve(solver=MOSEK)
self.assertItemsAlmostEqual(self.x.value, [-100, 1])

# Test a case when only basic solution is available
import mosek
prob = Problem(objective, constraints)
prob.solve(solver=MOSEK, mosek_params={mosek.iparam.optimizer: mosek.optimizertype.primal_simplex})
self.assertItemsAlmostEqual(self.x.value, [-100, 1])

else:
with self.assertRaises(Exception) as cm:
prob = Problem(Minimize(norm(self.x, 1)), [self.x == 0])
Expand Down

0 comments on commit cde8afb

Please sign in to comment.