Skip to content

Commit 9cea0e0

Browse files
okuchapoyamad
authored andcommitted
DOC: Add docstrings for linprog_simplex
1 parent 85e4c27 commit 9cea0e0

File tree

3 files changed

+265
-5
lines changed

3 files changed

+265
-5
lines changed

docs/source/optimize.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ Optimize
66

77
optimize/nelder_mead
88
optimize/root_finding
9+
optimize/linprog_simplex
910
optimize/pivoting
1011
optimize/scalar_maximization
Lines changed: 7 additions & 0 deletions
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:

quantecon/optimize/linprog_simplex.py

Lines changed: 257 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,89 @@
2020
def linprog_simplex(c, A_ub=np.empty((0, 0)), b_ub=np.empty((0,)),
2121
A_eq=np.empty((0, 0)), b_eq=np.empty((0,)), max_iter=10**6,
2222
tableau=None, basis=None, x=None, lambd=None):
23+
"""
24+
Solve a linear program in the following form by the simplex
25+
algorithm (with the lexicographic pivoting rule):
26+
27+
maximize::
28+
29+
c @ x
30+
31+
subject to::
32+
33+
A_ub @ x <= b_ub
34+
A_eq @ x == b_eq
35+
x >= 0
36+
37+
Parameters
38+
----------
39+
c : ndarray(float, ndim=1)
40+
ndarray of shape (n,).
41+
42+
A_ub : ndarray(float, ndim=2), optional
43+
ndarray of shape (m, n).
44+
45+
b_ub : ndarray(float, ndim=1), optional
46+
ndarray of shape (m,).
47+
48+
A_eq : ndarray(float, ndim=2), optional
49+
ndarray of shape (k, n).
50+
51+
b_eq : ndarray(float, ndim=1), optional
52+
ndarray of shape (k,).
53+
54+
max_iter : int, optional(default=10**6)
55+
Maximum number of iteration to perform.
56+
57+
tableau : ndarray(float, ndim=2), optional
58+
Temporary ndarray of shape (L+1, n+m+L+1) to store the tableau,
59+
where L=m+k. Modified in place.
60+
61+
basis : ndarray(int, ndim=1), optional
62+
Temporary ndarray of shape (L,) to store the basic variables.
63+
Modified in place.
64+
65+
x : ndarray(float, ndim=1), optional
66+
Output ndarray of shape (n,) to store the primal solution.
67+
68+
lambd : ndarray(float, ndim=1), optional
69+
Output ndarray of shape (L,) to store the dual solution.
70+
71+
Returns
72+
-------
73+
res : SimplexResult
74+
namedtuple consisting of the fields:
75+
76+
x : ndarray(float, ndim=1)
77+
ndarray of shape (n,) containing the primal solution.
78+
79+
lambd : ndarray(float, ndim=1)
80+
ndarray of shape (L,) containing the dual solution.
81+
82+
fun : float
83+
Value of the objective function.
84+
85+
success : bool
86+
True if the algorithm succeeded in finding an optimal
87+
solution.
88+
89+
status : int
90+
An integer representing the exit status of the
91+
optimization::
92+
93+
0 : Optimization terminated successfully
94+
1 : Iteration limit reached
95+
2 : Problem appears to be infeasible
96+
3 : Problem apperas to be unbounded
97+
98+
num_iter : int
99+
The number of iterations performed.
100+
101+
References
102+
----------
103+
* K. C. Border, "The Gauss–Jordan and Simplex Algorithms," 2004.
104+
105+
"""
23106
n, m, k = c.shape[0], A_ub.shape[0], A_eq.shape[0]
24107
L = m + k
25108

@@ -69,6 +152,81 @@ def linprog_simplex(c, A_ub=np.empty((0, 0)), b_ub=np.empty((0,)),
69152

70153
@jit(nopython=True, cache=True)
71154
def _initialize_tableau(A_ub, b_ub, A_eq, b_eq, tableau, basis):
155+
"""
156+
Initialize the `tableau` and `basis` arrays in place for Phase 1.
157+
158+
Suppose that the original linear program has the following form:
159+
160+
maximize::
161+
162+
c @ x
163+
164+
subject to::
165+
166+
A_ub @ x <= b_ub
167+
A_eq @ x == b_eq
168+
x >= 0
169+
170+
Let s be a vector of slack variables converting the inequality
171+
constraint to an equality constraint so that the problem turns to be
172+
the standard form:
173+
174+
maximize::
175+
176+
c @ x
177+
178+
subject to::
179+
180+
A_ub @ x + s == b_ub
181+
A_eq @ x == b_eq
182+
x, s >= 0
183+
184+
Then, let (z1, z2) be a vector of artificial variables for Phase 1:
185+
we solve the following LP:
186+
187+
maximize::
188+
189+
-(1 @ z1 + 1 @ z2)
190+
191+
subject to::
192+
193+
A_ub @ x + s + z1 == b_ub
194+
A_eq @ x + z2 == b_eq
195+
x, s, z1, z2 >= 0
196+
197+
The tableau needs to be of shape (L+1, n+m+L+1), where L=m+k.
198+
199+
Parameters
200+
----------
201+
A_ub : ndarray(float, ndim=2)
202+
ndarray of shape (m, n).
203+
204+
b_ub : ndarray(float, ndim=1)
205+
ndarray of shape (m,).
206+
207+
A_eq : ndarray(float, ndim=2)
208+
ndarray of shape (k, n).
209+
210+
b_eq : ndarray(float, ndim=1)
211+
ndarray of shape (k,).
212+
213+
tableau : ndarray(float, ndim=2)
214+
Empty ndarray of shape (L+1, n+m+L+1) to store the tableau.
215+
Modified in place.
216+
217+
basis : ndarray(int, ndim=1)
218+
Empty ndarray of shape (L,) to store the basic variables.
219+
Modified in place.
220+
221+
Returns
222+
-------
223+
tableau : ndarray(float, ndim=2)
224+
View to `tableau`.
225+
226+
basis : ndarray(int, ndim=1)
227+
View to `basis`.
228+
229+
"""
72230
m, k = A_ub.shape[0], A_eq.shape[0]
73231
L = m + k
74232
n = tableau.shape[1] - (m+L+1)
@@ -114,6 +272,27 @@ def _initialize_tableau(A_ub, b_ub, A_eq, b_eq, tableau, basis):
114272

115273
@jit(nopython=True, cache=True)
116274
def _set_criterion_row(c, basis, tableau):
275+
"""
276+
Modify the criterion row of the tableau for Phase 2.
277+
278+
Parameters
279+
----------
280+
c : ndarray(float, ndim=1)
281+
ndarray of shape (n,).
282+
283+
basis : ndarray(int, ndim=1)
284+
ndarray of shape (L,) containing the basis obtained by Phase 1.
285+
286+
tableau : ndarray(float, ndim=2)
287+
ndarray of shape (L+1, n+m+L+1) containing the tableau obtained
288+
by Phase 1. Modified in place.
289+
290+
Returns
291+
-------
292+
tableau : ndarray(float, ndim=2)
293+
View to `tableau`.
294+
295+
"""
117296
n = c.shape[0]
118297
L = basis.shape[0]
119298

@@ -136,11 +315,15 @@ def solve_tableau(tableau, basis, max_iter=10**6, skip_aux=True):
136315
137316
Used to solve a linear program in the following form:
138317
139-
maximize: c @ x
318+
maximize::
140319
141-
subject to: A_ub @ x <= b_ub
142-
A_eq @ x == b_eq
143-
x >= 0
320+
c @ x
321+
322+
subject to::
323+
324+
A_ub @ x <= b_ub
325+
A_eq @ x == b_eq
326+
x >= 0
144327
145328
where A_ub is of shape (m, n) and A_eq is of shape (k, n). Thus,
146329
`tableau` is of shape (L+1, n+m+L+1), where L=m+k, and
@@ -159,13 +342,24 @@ def solve_tableau(tableau, basis, max_iter=10**6, skip_aux=True):
159342
ndarray of shape (L,) containing the basic variables. Modified
160343
in place.
161344
162-
max_iter : scalar(int), optional(default=10**6)
345+
max_iter : int, optional(default=10**6)
163346
Maximum number of pivoting steps.
164347
165348
skip_aux : bool, optional(default=True)
166349
Whether to skip the coefficients of the auxiliary (or
167350
artificial) variables in pivot column selection.
168351
352+
Returns
353+
-------
354+
success : bool
355+
True if the algorithm succeeded in finding an optimal solution.
356+
357+
status : int
358+
An integer representing the exit status of the optimization.
359+
360+
num_iter : int
361+
The number of iterations performed.
362+
169363
"""
170364
L = tableau.shape[0] - 1
171365

@@ -203,6 +397,33 @@ def solve_tableau(tableau, basis, max_iter=10**6, skip_aux=True):
203397

204398
@jit(nopython=True, cache=True)
205399
def _pivot_col(tableau, skip_aux):
400+
"""
401+
Choose the column containing the pivot element: the column
402+
containing the maximum positive element in the last row of the
403+
tableau.
404+
405+
`skip_aux` should be True in phase 1, and False in phase 2.
406+
407+
Parameters
408+
----------
409+
tableau : ndarray(float, ndim=2)
410+
ndarray of shape (L+1, n+m+L+1) containing the tableau.
411+
412+
skip_aux : bool
413+
Whether to skip the coefficients of the auxiliary (or
414+
artificial) variables in pivot column selection.
415+
416+
Returns
417+
-------
418+
found : bool
419+
True iff there is a positive element in the last row of the
420+
tableau (and then pivotting should be conducted).
421+
422+
pivcol : int
423+
The index of column containing the pivot element. (-1 if `found
424+
== False`.)
425+
426+
"""
206427
L = tableau.shape[0] - 1
207428
criterion_row_stop = tableau.shape[1] - 1
208429
if skip_aux:
@@ -222,6 +443,37 @@ def _pivot_col(tableau, skip_aux):
222443

223444
@jit(nopython=True, cache=True)
224445
def get_solution(tableau, basis, x, lambd, b_signs):
446+
"""
447+
Fetch the optimal solution and value from an optimal tableau.
448+
449+
Parameters
450+
----------
451+
tableau : ndarray(float, ndim=2)
452+
ndarray of shape (L+1, n+m+L+1) containing the optimal tableau,
453+
where L=m+k.
454+
455+
basis : ndarray(int, ndim=1)
456+
Empty ndarray of shape (L,) to store the basic variables.
457+
Modified in place.
458+
459+
x : ndarray(float, ndim=1)
460+
Empty ndarray of shape (n,) to store the primal solution.
461+
Modified in place.
462+
463+
lambd : ndarray(float, ndim=1)
464+
Empty ndarray of shape (L,) to store the dual solution. Modified
465+
in place.
466+
467+
b_signs : ndarray(bool, ndim=1)
468+
ndarray of shape (L,) whose i-th element is True iff the i-th
469+
element of the vector (b_ub, b_eq) is positive.
470+
471+
Returns
472+
-------
473+
fun : float
474+
The optimal value.
475+
476+
"""
225477
n, L = x.size, lambd.size
226478
aux_start = tableau.shape[1] - L - 1
227479

0 commit comments

Comments
 (0)