Skip to content

Commit 0adb3ec

Browse files
authored
Merge pull request #727 from QuantEcon/fix-linprog
FIX: linprog_simplex: Fix bug in Phase 1
2 parents d7c2f52 + 85ca2a1 commit 0adb3ec

File tree

2 files changed

+58
-7
lines changed

2 files changed

+58
-7
lines changed

quantecon/optimize/linprog_simplex.py

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -163,14 +163,9 @@ def linprog_simplex(c, A_ub=np.empty((0, 0)), b_ub=np.empty((0,)),
163163

164164
# Phase 1
165165
success, status, num_iter_1 = \
166-
solve_tableau(tableau, basis, max_iter, skip_aux=False,
167-
piv_options=piv_options)
166+
solve_phase_1(tableau, basis, max_iter, piv_options=piv_options)
168167
num_iter += num_iter_1
169-
if not success: # max_iter exceeded
170-
return SimplexResult(x, lambd, fun, success, status, num_iter)
171-
if tableau[-1, -1] > piv_options.fea_tol: # Infeasible
172-
success = False
173-
status = 2
168+
if not success:
174169
return SimplexResult(x, lambd, fun, success, status, num_iter)
175170

176171
# Modify the criterion row for Phase 2
@@ -442,6 +437,50 @@ def solve_tableau(tableau, basis, max_iter=10**6, skip_aux=True,
442437
return success, status, num_iter
443438

444439

440+
@jit(nopython=True, cache=True)
441+
def solve_phase_1(tableau, basis, max_iter=10**6, piv_options=PivOptions()):
442+
"""
443+
Perform the simplex algorithm for Phase 1 on a given tableau in
444+
canonical form, by calling `solve_tableau` with `skip_aux=False`.
445+
446+
Parameters
447+
----------
448+
See `solve_tableau`.
449+
450+
Returns
451+
-------
452+
See `solve_tableau`.
453+
454+
"""
455+
L = tableau.shape[0] - 1
456+
nm = tableau.shape[1] - (L+1) # n + m
457+
458+
success, status, num_iter_1 = \
459+
solve_tableau(tableau, basis, max_iter, skip_aux=False,
460+
piv_options=piv_options)
461+
462+
if not success: # max_iter exceeded
463+
return success, status, num_iter_1
464+
if tableau[-1, -1] > piv_options.fea_tol: # Infeasible
465+
success = False
466+
status = 2
467+
return success, status, num_iter_1
468+
469+
# Check artifial variables have been eliminated
470+
tol_piv = piv_options.tol_piv
471+
for i in range(L):
472+
if basis[i] >= nm: # Artifial variable not eliminated
473+
for j in range(nm):
474+
if tableau[i, j] < -tol_piv or \
475+
tableau[i, j] > tol_piv: # Treated nonzero
476+
_pivoting(tableau, j, i)
477+
basis[i] = j
478+
num_iter_1 += 1
479+
break
480+
481+
return success, status, num_iter_1
482+
483+
445484
@jit(nopython=True, cache=True)
446485
def _pivot_col(tableau, skip_aux, piv_options):
447486
"""

quantecon/optimize/tests/test_linprog_simplex.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,3 +239,15 @@ def test_bug_8662(self):
239239
desired_fun = -36.0000000000
240240
res = linprog_simplex(c, A_ub=A_ub, b_ub=b_ub)
241241
_assert_success(res, c, b_ub=b_ub, desired_fun=desired_fun)
242+
243+
244+
class TestLinprogSimplex:
245+
def test_phase_1_bug_725(self):
246+
# Identified a bug in Phase 1
247+
# https://github.com/QuantEcon/QuantEcon.py/issues/725
248+
c = np.array([-4.09555556, 4.59044444])
249+
A_ub = np.array([[1, 0.1], [-1, -0.1], [1, 1]])
250+
b_ub = np.array([9.1, -0.1, 0.1])
251+
desired_x = [0.1, 0]
252+
res = linprog_simplex(c, A_ub=A_ub, b_ub=b_ub)
253+
_assert_success(res, c, b_ub=b_ub, desired_x=desired_x)

0 commit comments

Comments
 (0)