2020def 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 )
71154def _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 )
116274def _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 )
205399def _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 )
224445def 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