88from .pivoting import _pivoting , _lex_min_ratio_test
99
1010
11- FEA_TOL = 1e-6
12-
13-
1411SimplexResult = namedtuple (
1512 'SimplexResult' , ['x' , 'lambd' , 'fun' , 'success' , 'status' , 'num_iter' ]
1613)
1714
15+ FEA_TOL = 1e-6
16+ TOL_PIV = 1e-7
17+ TOL_RATIO_DIFF = 1e-13
18+
19+ PivOptions = namedtuple (
20+ 'PivOptions' , ['fea_tol' , 'tol_piv' , 'tol_ratio_diff' ]
21+ )
22+ PivOptions .__new__ .__defaults__ = (FEA_TOL , TOL_PIV , TOL_RATIO_DIFF )
23+
1824
1925@jit (nopython = True , cache = True )
2026def linprog_simplex (c , A_ub = np .empty ((0 , 0 )), b_ub = np .empty ((0 ,)),
21- A_eq = np .empty ((0 , 0 )), b_eq = np .empty ((0 ,)), max_iter = 10 ** 6 ,
27+ A_eq = np .empty ((0 , 0 )), b_eq = np .empty ((0 ,)),
28+ max_iter = 10 ** 6 , piv_options = PivOptions (),
2229 tableau = None , basis = None , x = None , lambd = None ):
2330 """
2431 Solve a linear program in the following form by the simplex
@@ -54,6 +61,19 @@ def linprog_simplex(c, A_ub=np.empty((0, 0)), b_ub=np.empty((0,)),
5461 max_iter : int, optional(default=10**6)
5562 Maximum number of iteration to perform.
5663
64+ piv_options : PivOptions, optional
65+ PivOptions namedtuple to set the following tolerance values:
66+
67+ fea_tol : float
68+ Feasibility tolerance (default={FEA_TOL}).
69+
70+ tol_piv : float
71+ Pivot tolerance (default={TOL_PIV}).
72+
73+ tol_ratio_diff : float
74+ Tolerance used in the the lexicographic pivoting
75+ (default={TOL_RATIO_DIFF}).
76+
5777 tableau : ndarray(float, ndim=2), optional
5878 Temporary ndarray of shape (L+1, n+m+L+1) to store the tableau,
5979 where L=m+k. Modified in place.
@@ -129,11 +149,12 @@ def linprog_simplex(c, A_ub=np.empty((0, 0)), b_ub=np.empty((0,)),
129149
130150 # Phase 1
131151 success , status , num_iter_1 = \
132- solve_tableau (tableau , basis , max_iter , skip_aux = False )
152+ solve_tableau (tableau , basis , max_iter , skip_aux = False ,
153+ piv_options = piv_options )
133154 num_iter += num_iter_1
134155 if not success : # max_iter exceeded
135156 return SimplexResult (x , lambd , fun , success , status , num_iter )
136- if tableau [- 1 , - 1 ] > FEA_TOL : # Infeasible
157+ if tableau [- 1 , - 1 ] > piv_options . fea_tol : # Infeasible
137158 success = False
138159 status = 2
139160 return SimplexResult (x , lambd , fun , success , status , num_iter )
@@ -143,13 +164,19 @@ def linprog_simplex(c, A_ub=np.empty((0, 0)), b_ub=np.empty((0,)),
143164
144165 # Phase 2
145166 success , status , num_iter_2 = \
146- solve_tableau (tableau , basis , max_iter - num_iter , skip_aux = True )
167+ solve_tableau (tableau , basis , max_iter - num_iter , skip_aux = True ,
168+ piv_options = piv_options )
147169 num_iter += num_iter_2
148170 fun = get_solution (tableau , basis , x , lambd , b_signs )
149171
150172 return SimplexResult (x , lambd , fun , success , status , num_iter )
151173
152174
175+ linprog_simplex .__doc__ = linprog_simplex .__doc__ .format (
176+ FEA_TOL = FEA_TOL , TOL_PIV = TOL_PIV , TOL_RATIO_DIFF = TOL_RATIO_DIFF
177+ )
178+
179+
153180@jit (nopython = True , cache = True )
154181def _initialize_tableau (A_ub , b_ub , A_eq , b_eq , tableau , basis ):
155182 """
@@ -309,7 +336,8 @@ def _set_criterion_row(c, basis, tableau):
309336
310337
311338@jit (nopython = True , cache = True )
312- def solve_tableau (tableau , basis , max_iter = 10 ** 6 , skip_aux = True ):
339+ def solve_tableau (tableau , basis , max_iter = 10 ** 6 , skip_aux = True ,
340+ piv_options = PivOptions ()):
313341 """
314342 Perform the simplex algorithm on a given tableau in canonical form.
315343
@@ -349,6 +377,9 @@ def solve_tableau(tableau, basis, max_iter=10**6, skip_aux=True):
349377 Whether to skip the coefficients of the auxiliary (or
350378 artificial) variables in pivot column selection.
351379
380+ piv_options : PivOptions, optional
381+ PivOptions namedtuple to set the tolerance values.
382+
352383 Returns
353384 -------
354385 success : bool
@@ -373,16 +404,18 @@ def solve_tableau(tableau, basis, max_iter=10**6, skip_aux=True):
373404 while num_iter < max_iter :
374405 num_iter += 1
375406
376- pivcol_found , pivcol = _pivot_col (tableau , skip_aux )
407+ pivcol_found , pivcol = _pivot_col (tableau , skip_aux , piv_options )
377408
378409 if not pivcol_found : # Optimal
379410 success = True
380411 status = 0
381412 break
382413
383414 aux_start = tableau .shape [1 ] - L - 1
384- pivrow_found , pivrow = _lex_min_ratio_test (tableau [:- 1 , :], pivcol ,
385- aux_start , argmins )
415+ pivrow_found , pivrow = _lex_min_ratio_test (
416+ tableau [:- 1 , :], pivcol , aux_start , argmins ,
417+ piv_options .tol_piv , piv_options .tol_ratio_diff
418+ )
386419
387420 if not pivrow_found : # Unbounded
388421 success = False
@@ -396,7 +429,7 @@ def solve_tableau(tableau, basis, max_iter=10**6, skip_aux=True):
396429
397430
398431@jit (nopython = True , cache = True )
399- def _pivot_col (tableau , skip_aux ):
432+ def _pivot_col (tableau , skip_aux , piv_options ):
400433 """
401434 Choose the column containing the pivot element: the column
402435 containing the maximum positive element in the last row of the
@@ -413,6 +446,9 @@ def _pivot_col(tableau, skip_aux):
413446 Whether to skip the coefficients of the auxiliary (or
414447 artificial) variables in pivot column selection.
415448
449+ piv_options : PivOptions, optional
450+ PivOptions namedtuple to set the tolerance values.
451+
416452 Returns
417453 -------
418454 found : bool
@@ -431,7 +467,7 @@ def _pivot_col(tableau, skip_aux):
431467
432468 found = False
433469 pivcol = - 1
434- coeff = FEA_TOL
470+ coeff = piv_options . fea_tol
435471 for j in range (criterion_row_stop ):
436472 if tableau [- 1 , j ] > coeff :
437473 coeff = tableau [- 1 , j ]
0 commit comments