Skip to content

Commit

Permalink
BUG: fix linprog revised simplex docstring problem failure (scipy#10135)
Browse files Browse the repository at this point in the history
* BUG: fix linprog revised simplex docstring problem failure
linprog with method='revised simplex' failed docstring problem. The cause
was that option 'disp'=True executes _postsolve, which changes the
solution vector. Simple fix was to copy the solution vector before passing
it into _postsolve.

* BUG: optimize: adds tests for bug scipy#10124

* BUG: optimize: fix linprog docstring failure for interior-point

* BUG:optimize:fix linprog interior-point not calling callback on iteration 0

* ENH:optimize:linprog_ip convert displayed variables to float in _display_iter

* ENH:optimize:_linprog_rs use new _postsolve copy option
  • Loading branch information
mdhaber authored and tylerjereddy committed May 8, 2019
1 parent 0b4f018 commit a860f6d
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 11 deletions.
23 changes: 16 additions & 7 deletions scipy/optimize/_linprog_ip.py
Expand Up @@ -22,6 +22,7 @@
import numpy as np
import scipy as sp
import scipy.sparse as sps
import numbers
from warnings import warn
from scipy.linalg import LinAlgError
from .optimize import OptimizeWarning, OptimizeResult, _check_unknown_options
Expand Down Expand Up @@ -557,12 +558,12 @@ def _display_iter(rho_p, rho_d, rho_g, alpha, rho_mu, obj, header=False):
# no clue why this works
fmt = '{0:<20.13}{1:<20.13}{2:<20.13}{3:<17.13}{4:<20.13}{5:<20.13}'
print(fmt.format(
rho_p,
rho_d,
rho_g,
alpha,
rho_mu,
obj))
float(rho_p),
float(rho_d),
float(rho_g),
float(alpha) if isinstance(alpha, numbers.Number) else alpha,
float(rho_mu),
float(obj)))


def _ip_hsd(A, b, c, c0, alpha0, beta, maxiter, disp, tol, sparse, lstsq,
Expand Down Expand Up @@ -722,6 +723,14 @@ def _ip_hsd(A, b, c, c0, alpha0, beta, maxiter, disp, tol, sparse, lstsq,

if disp:
_display_iter(rho_p, rho_d, rho_g, "-", rho_mu, obj, header=True)
if callback is not None:
x_o, fun, slack, con, _, _ = _postsolve(x/tau, *_T_o,
tol=tol)
res = OptimizeResult({'x': x_o, 'fun': fun, 'slack': slack,
'con': con, 'nit': iteration, 'phase': 1,
'complete': False, 'status': 0,
'message': "", 'success': False})
callback(res)

status = 0
message = "Optimization terminated successfully."
Expand Down Expand Up @@ -794,7 +803,7 @@ def eta(g=gamma):
go = rho_p > tol or rho_d > tol or rho_A > tol

if disp:
_display_iter(rho_p, rho_d, rho_g, alpha, float(rho_mu), obj)
_display_iter(rho_p, rho_d, rho_g, alpha, rho_mu, obj)
if callback is not None:
x_o, fun, slack, con, _, _ = _postsolve(x/tau, *_T_o,
tol=tol)
Expand Down
3 changes: 1 addition & 2 deletions scipy/optimize/_linprog_rs.py
Expand Up @@ -93,7 +93,6 @@ def _phase_one(A, b, x0, maxiter, tol, maxupdate, mast, pivot, callback=None,
basis = basis[keep_rows]
x = x[:n]
m = A.shape[0]

return x, basis, A, b, residual, status, iter_k


Expand Down Expand Up @@ -342,7 +341,7 @@ def _phase_two(c, A, x, b, maxiter, tol, maxupdate, mast, pivot, iteration=0,
phase = 2
x_postsolve = x
x_o, fun, slack, con, _, _ = _postsolve(x_postsolve, *_T_o,
tol=tol)
tol=tol, copy=True)

if callback is not None:
res = OptimizeResult({'x': x_o, 'fun': fun, 'slack': slack,
Expand Down
6 changes: 4 additions & 2 deletions scipy/optimize/_linprog_util.py
Expand Up @@ -1114,7 +1114,7 @@ def _display_summary(message, status, fun, iteration):


def _postsolve(x, c, A_ub=None, b_ub=None, A_eq=None, b_eq=None, bounds=None,
complete=False, undo=[], tol=1e-8):
complete=False, undo=[], tol=1e-8, copy=False):
"""
Given solution x to presolved, standard form linear program x, add
fixed variables back into the problem and undo the variable substitutions
Expand Down Expand Up @@ -1187,7 +1187,9 @@ def _postsolve(x, c, A_ub=None, b_ub=None, A_eq=None, b_eq=None, bounds=None,
x = x.tolist()
for i, val in zip(undo[0], undo[1]):
x.insert(i, val)
x = np.array(x)
copy = True
if copy:
x = np.array(x, copy=True)

# now undo variable substitutions
# if "complete", problem was solved in presolve; don't do anything here
Expand Down
19 changes: 19 additions & 0 deletions scipy/optimize/tests/test_linprog.py
Expand Up @@ -1301,6 +1301,25 @@ def test_bug_8973_2(self):
method=self.method, options=self.options)
_assert_success(res, desired_x=[-2], desired_fun=0)

def test_bug_10124(self):
"""
Test for linprog docstring problem
'disp'=True caused revised simplex failure
"""
c = np.zeros(1)
A_ub = np.array([[1]])
b_ub = np.array([-2])
bounds = (None, None)
c = [-1, 4]
A_ub = [[-3, 1], [1, 2]]
b_ub = [6, 4]
bounds = [(None, None), (-3, None)]
o = {"disp": True}
o.update(self.options)
res = linprog(c, A_ub, b_ub, A_eq, b_eq, bounds,
method=self.method, options=self.options)
_assert_success(res, desired_x=[10, -3], desired_fun=-22)

#########################
# Method-specific Tests #
#########################
Expand Down

0 comments on commit a860f6d

Please sign in to comment.