Skip to content

Commit 760dc3c

Browse files
authored
Merge pull request #532 from oyamad/linprog_simplex
EHN: Add Numba-jitted linprog solver
2 parents 83ae0a2 + ba02284 commit 760dc3c

File tree

8 files changed

+981
-148
lines changed

8 files changed

+981
-148
lines changed

Diff for: docs/source/optimize.rst

+2
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@ Optimize
66

77
optimize/nelder_mead
88
optimize/root_finding
9+
optimize/linprog_simplex
10+
optimize/pivoting
911
optimize/scalar_maximization

Diff for: docs/source/optimize/linprog_simplex.rst

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
linprog_simplex
2+
===============
3+
4+
.. automodule:: quantecon.optimize.linprog_simplex
5+
:members:
6+
:undoc-members:
7+
:show-inheritance:

Diff for: docs/source/optimize/pivoting.rst

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
pivoting
2+
========
3+
4+
.. automodule:: quantecon.optimize.pivoting
5+
:members:
6+
:undoc-members:
7+
:show-inheritance:

Diff for: quantecon/game_theory/lemke_howson.py

+3-147
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,7 @@
77
import numpy as np
88
from numba import jit
99
from .utilities import NashResult
10-
11-
12-
TOL_PIV = 1e-10
13-
TOL_RATIO_DIFF = 1e-15
10+
from ..optimize.pivoting import _pivoting, _lex_min_ratio_test
1411

1512

1613
def lemke_howson(g, init_pivot=0, max_iter=10**6, capping=None,
@@ -398,8 +395,8 @@ def _lemke_howson_tbl(tableaux, bases, init_pivot, max_iter):
398395
while True:
399396
for pl in pls:
400397
# Determine the leaving variable
401-
row_min = _lex_min_ratio_test(tableaux[pl], pivot,
402-
slack_starts[pl], argmins)
398+
_, row_min = _lex_min_ratio_test(tableaux[pl], pivot,
399+
slack_starts[pl], argmins)
403400

404401
# Pivoting step: modify tableau in place
405402
_pivoting(tableaux[pl], pivot, row_min)
@@ -421,147 +418,6 @@ def _lemke_howson_tbl(tableaux, bases, init_pivot, max_iter):
421418
return converged, num_iter
422419

423420

424-
@jit(nopython=True, cache=True)
425-
def _pivoting(tableau, pivot, pivot_row):
426-
"""
427-
Perform a pivoting step. Modify `tableau` in place.
428-
429-
Parameters
430-
----------
431-
tableau : ndarray(float, ndim=2)
432-
Array containing the tableau.
433-
434-
pivot : scalar(int)
435-
Pivot.
436-
437-
pivot_row : scalar(int)
438-
Pivot row index.
439-
440-
Returns
441-
-------
442-
tableau : ndarray(float, ndim=2)
443-
View to `tableau`.
444-
445-
"""
446-
nrows, ncols = tableau.shape
447-
448-
pivot_elt = tableau[pivot_row, pivot]
449-
for j in range(ncols):
450-
tableau[pivot_row, j] /= pivot_elt
451-
452-
for i in range(nrows):
453-
if i == pivot_row:
454-
continue
455-
multiplier = tableau[i, pivot]
456-
if multiplier == 0:
457-
continue
458-
for j in range(ncols):
459-
tableau[i, j] -= tableau[pivot_row, j] * multiplier
460-
461-
return tableau
462-
463-
464-
@jit(nopython=True, cache=True)
465-
def _min_ratio_test_no_tie_breaking(tableau, pivot, test_col,
466-
argmins, num_candidates):
467-
"""
468-
Perform the minimum ratio test, without tie breaking, for the
469-
candidate rows in `argmins[:num_candidates]`. Return the number
470-
`num_argmins` of the rows minimizing the ratio and store thier
471-
indices in `argmins[:num_argmins]`.
472-
473-
Parameters
474-
----------
475-
tableau : ndarray(float, ndim=2)
476-
Array containing the tableau.
477-
478-
pivot : scalar(int)
479-
Pivot.
480-
481-
test_col : scalar(int)
482-
Index of the column used in the test.
483-
484-
argmins : ndarray(int, ndim=1)
485-
Array containing the indices of the candidate rows. Modified in
486-
place to store the indices of minimizing rows.
487-
488-
num_candidates : scalar(int)
489-
Number of candidate rows in `argmins`.
490-
491-
Returns
492-
-------
493-
num_argmins : scalar(int)
494-
Number of minimizing rows.
495-
496-
"""
497-
ratio_min = np.inf
498-
num_argmins = 0
499-
500-
for k in range(num_candidates):
501-
i = argmins[k]
502-
if tableau[i, pivot] <= TOL_PIV: # Treated as nonpositive
503-
continue
504-
ratio = tableau[i, test_col] / tableau[i, pivot]
505-
if ratio > ratio_min + TOL_RATIO_DIFF: # Ratio large for i
506-
continue
507-
elif ratio < ratio_min - TOL_RATIO_DIFF: # Ratio smaller for i
508-
ratio_min = ratio
509-
num_argmins = 1
510-
else: # Ratio equal
511-
num_argmins += 1
512-
argmins[num_argmins-1] = i
513-
514-
return num_argmins
515-
516-
517-
@jit(nopython=True, cache=True)
518-
def _lex_min_ratio_test(tableau, pivot, slack_start, argmins):
519-
"""
520-
Perform the lexico-minimum ratio test.
521-
522-
Parameters
523-
----------
524-
tableau : ndarray(float, ndim=2)
525-
Array containing the tableau.
526-
527-
pivot : scalar(int)
528-
Pivot.
529-
530-
slack_start : scalar(int)
531-
First index for the slack variables.
532-
533-
argmins : ndarray(int, ndim=1)
534-
Empty array used to store the row indices. Its length must be no
535-
smaller than the number of the rows of `tableau`.
536-
537-
Returns
538-
-------
539-
row_min : scalar(int)
540-
Index of the row with the lexico-minimum ratio.
541-
542-
"""
543-
nrows = tableau.shape[0]
544-
num_candidates = nrows
545-
546-
# Initialize `argmins`
547-
for i in range(nrows):
548-
argmins[i] = i
549-
550-
num_argmins = _min_ratio_test_no_tie_breaking(tableau, pivot, -1,
551-
argmins, num_candidates)
552-
if num_argmins == 1:
553-
return argmins[0]
554-
555-
for j in range(slack_start, slack_start+nrows):
556-
if j == pivot:
557-
continue
558-
num_argmins = _min_ratio_test_no_tie_breaking(tableau, pivot, j,
559-
argmins, num_argmins)
560-
if num_argmins == 1:
561-
break
562-
return argmins[0]
563-
564-
565421
@jit(nopython=True, cache=True)
566422
def _get_mixed_actions(tableaux, bases):
567423
"""

Diff for: quantecon/optimize/__init__.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
"""
33
Initialization of the optimize subpackage
44
"""
5-
5+
from .linprog_simplex import (
6+
linprog_simplex, solve_tableau, get_solution, PivOptions
7+
)
68
from .scalar_maximization import brent_max
79
from .nelder_mead import nelder_mead
810
from .root_finding import newton, newton_halley, newton_secant, bisect, brentq

0 commit comments

Comments
 (0)