_linprog_util.py 52 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328
  1. """
  2. Method agnostic utility functions for linear progamming
  3. """
  4. import numpy as np
  5. import scipy.sparse as sps
  6. from warnings import warn
  7. from .optimize import OptimizeWarning
  8. from scipy.optimize._remove_redundancy import (
  9. _remove_redundancy, _remove_redundancy_sparse, _remove_redundancy_dense
  10. )
  11. def _check_sparse_inputs(options, A_ub, A_eq):
  12. """
  13. Check the provided ``A_ub`` and ``A_eq`` matrices conform to the specified
  14. optional sparsity variables.
  15. Parameters
  16. ----------
  17. A_ub : 2D array, optional
  18. 2D array such that ``A_ub @ x`` gives the values of the upper-bound
  19. inequality constraints at ``x``.
  20. A_eq : 2D array, optional
  21. 2D array such that ``A_eq @ x`` gives the values of the equality
  22. constraints at ``x``.
  23. options : dict
  24. A dictionary of solver options. All methods accept the following
  25. generic options:
  26. maxiter : int
  27. Maximum number of iterations to perform.
  28. disp : bool
  29. Set to True to print convergence messages.
  30. For method-specific options, see :func:`show_options('linprog')`.
  31. Returns
  32. -------
  33. A_ub : 2D array, optional
  34. 2D array such that ``A_ub @ x`` gives the values of the upper-bound
  35. inequality constraints at ``x``.
  36. A_eq : 2D array, optional
  37. 2D array such that ``A_eq @ x`` gives the values of the equality
  38. constraints at ``x``.
  39. options : dict
  40. A dictionary of solver options. All methods accept the following
  41. generic options:
  42. maxiter : int
  43. Maximum number of iterations to perform.
  44. disp : bool
  45. Set to True to print convergence messages.
  46. For method-specific options, see :func:`show_options('linprog')`.
  47. """
  48. # This is an undocumented option for unit testing sparse presolve
  49. _sparse_presolve = options.pop('_sparse_presolve', False)
  50. if _sparse_presolve and A_eq is not None:
  51. A_eq = sps.coo_matrix(A_eq)
  52. if _sparse_presolve and A_ub is not None:
  53. A_ub = sps.coo_matrix(A_ub)
  54. sparse = options.get('sparse', False)
  55. if not sparse and (sps.issparse(A_eq) or sps.issparse(A_ub)):
  56. options['sparse'] = True
  57. warn("Sparse constraint matrix detected; setting 'sparse':True.",
  58. OptimizeWarning)
  59. return options, A_ub, A_eq
  60. def _clean_inputs(c, A_ub=None, b_ub=None, A_eq=None, b_eq=None, bounds=None):
  61. """
  62. Given user inputs for a linear programming problem, return the
  63. objective vector, upper bound constraints, equality constraints,
  64. and simple bounds in a preferred format.
  65. Parameters
  66. ----------
  67. c : 1D array
  68. Coefficients of the linear objective function to be minimized.
  69. A_ub : 2D array, optional
  70. 2D array such that ``A_ub @ x`` gives the values of the upper-bound
  71. inequality constraints at ``x``.
  72. b_ub : 1D array, optional
  73. 1D array of values representing the upper-bound of each inequality
  74. constraint (row) in ``A_ub``.
  75. A_eq : 2D array, optional
  76. 2D array such that ``A_eq @ x`` gives the values of the equality
  77. constraints at ``x``.
  78. b_eq : 1D array, optional
  79. 1D array of values representing the RHS of each equality constraint
  80. (row) in ``A_eq``.
  81. bounds : sequence, optional
  82. ``(min, max)`` pairs for each element in ``x``, defining
  83. the bounds on that parameter. Use None for one of ``min`` or
  84. ``max`` when there is no bound in that direction. By default
  85. bounds are ``(0, None)`` (non-negative).
  86. If a sequence containing a single tuple is provided, then ``min`` and
  87. ``max`` will be applied to all variables in the problem.
  88. Returns
  89. -------
  90. c : 1D array
  91. Coefficients of the linear objective function to be minimized.
  92. A_ub : 2D array, optional
  93. 2D array such that ``A_ub @ x`` gives the values of the upper-bound
  94. inequality constraints at ``x``.
  95. b_ub : 1D array, optional
  96. 1D array of values representing the upper-bound of each inequality
  97. constraint (row) in ``A_ub``.
  98. A_eq : 2D array, optional
  99. 2D array such that ``A_eq @ x`` gives the values of the equality
  100. constraints at ``x``.
  101. b_eq : 1D array, optional
  102. 1D array of values representing the RHS of each equality constraint
  103. (row) in ``A_eq``.
  104. bounds : sequence of tuples
  105. ``(min, max)`` pairs for each element in ``x``, defining
  106. the bounds on that parameter. Use None for each of ``min`` or
  107. ``max`` when there is no bound in that direction. By default
  108. bounds are ``(0, None)`` (non-negative).
  109. """
  110. try:
  111. if c is None:
  112. raise TypeError
  113. try:
  114. c = np.asarray(c, dtype=float).copy().squeeze()
  115. except BaseException: # typically a ValueError and shouldn't be, IMO
  116. raise TypeError
  117. if c.size == 1:
  118. c = c.reshape((-1))
  119. n_x = len(c)
  120. if n_x == 0 or len(c.shape) != 1:
  121. raise ValueError(
  122. "Invalid input for linprog: c should be a 1D array; it must "
  123. "not have more than one non-singleton dimension")
  124. if not(np.isfinite(c).all()):
  125. raise ValueError(
  126. "Invalid input for linprog: c must not contain values "
  127. "inf, nan, or None")
  128. except TypeError:
  129. raise TypeError(
  130. "Invalid input for linprog: c must be a 1D array of numerical "
  131. "coefficients")
  132. try:
  133. try:
  134. if sps.issparse(A_eq) or sps.issparse(A_ub):
  135. A_ub = sps.coo_matrix(
  136. (0, n_x), dtype=float) if A_ub is None else sps.coo_matrix(
  137. A_ub, dtype=float).copy()
  138. else:
  139. A_ub = np.zeros(
  140. (0, n_x), dtype=float) if A_ub is None else np.asarray(
  141. A_ub, dtype=float).copy()
  142. except BaseException:
  143. raise TypeError
  144. n_ub = A_ub.shape[0]
  145. if len(A_ub.shape) != 2 or A_ub.shape[1] != len(c):
  146. raise ValueError(
  147. "Invalid input for linprog: A_ub must have exactly two "
  148. "dimensions, and the number of columns in A_ub must be "
  149. "equal to the size of c ")
  150. if (sps.issparse(A_ub) and not np.isfinite(A_ub.data).all()
  151. or not sps.issparse(A_ub) and not np.isfinite(A_ub).all()):
  152. raise ValueError(
  153. "Invalid input for linprog: A_ub must not contain values "
  154. "inf, nan, or None")
  155. except TypeError:
  156. raise TypeError(
  157. "Invalid input for linprog: A_ub must be a numerical 2D array "
  158. "with each row representing an upper bound inequality constraint")
  159. try:
  160. try:
  161. b_ub = np.array(
  162. [], dtype=float) if b_ub is None else np.asarray(
  163. b_ub, dtype=float).copy().squeeze()
  164. except BaseException:
  165. raise TypeError
  166. if b_ub.size == 1:
  167. b_ub = b_ub.reshape((-1))
  168. if len(b_ub.shape) != 1:
  169. raise ValueError(
  170. "Invalid input for linprog: b_ub should be a 1D array; it "
  171. "must not have more than one non-singleton dimension")
  172. if len(b_ub) != n_ub:
  173. raise ValueError(
  174. "Invalid input for linprog: The number of rows in A_ub must "
  175. "be equal to the number of values in b_ub")
  176. if not(np.isfinite(b_ub).all()):
  177. raise ValueError(
  178. "Invalid input for linprog: b_ub must not contain values "
  179. "inf, nan, or None")
  180. except TypeError:
  181. raise TypeError(
  182. "Invalid input for linprog: b_ub must be a 1D array of "
  183. "numerical values, each representing the upper bound of an "
  184. "inequality constraint (row) in A_ub")
  185. try:
  186. try:
  187. if sps.issparse(A_eq) or sps.issparse(A_ub):
  188. A_eq = sps.coo_matrix(
  189. (0, n_x), dtype=float) if A_eq is None else sps.coo_matrix(
  190. A_eq, dtype=float).copy()
  191. else:
  192. A_eq = np.zeros(
  193. (0, n_x), dtype=float) if A_eq is None else np.asarray(
  194. A_eq, dtype=float).copy()
  195. except BaseException:
  196. raise TypeError
  197. n_eq = A_eq.shape[0]
  198. if len(A_eq.shape) != 2 or A_eq.shape[1] != len(c):
  199. raise ValueError(
  200. "Invalid input for linprog: A_eq must have exactly two "
  201. "dimensions, and the number of columns in A_eq must be "
  202. "equal to the size of c ")
  203. if (sps.issparse(A_eq) and not np.isfinite(A_eq.data).all()
  204. or not sps.issparse(A_eq) and not np.isfinite(A_eq).all()):
  205. raise ValueError(
  206. "Invalid input for linprog: A_eq must not contain values "
  207. "inf, nan, or None")
  208. except TypeError:
  209. raise TypeError(
  210. "Invalid input for linprog: A_eq must be a 2D array with each "
  211. "row representing an equality constraint")
  212. try:
  213. try:
  214. b_eq = np.array(
  215. [], dtype=float) if b_eq is None else np.asarray(
  216. b_eq, dtype=float).copy().squeeze()
  217. except BaseException:
  218. raise TypeError
  219. if b_eq.size == 1:
  220. b_eq = b_eq.reshape((-1))
  221. if len(b_eq.shape) != 1:
  222. raise ValueError(
  223. "Invalid input for linprog: b_eq should be a 1D array; it "
  224. "must not have more than one non-singleton dimension")
  225. if len(b_eq) != n_eq:
  226. raise ValueError(
  227. "Invalid input for linprog: the number of rows in A_eq "
  228. "must be equal to the number of values in b_eq")
  229. if not(np.isfinite(b_eq).all()):
  230. raise ValueError(
  231. "Invalid input for linprog: b_eq must not contain values "
  232. "inf, nan, or None")
  233. except TypeError:
  234. raise TypeError(
  235. "Invalid input for linprog: b_eq must be a 1D array of "
  236. "numerical values, each representing the right hand side of an "
  237. "equality constraints (row) in A_eq")
  238. # "If a sequence containing a single tuple is provided, then min and max
  239. # will be applied to all variables in the problem."
  240. # linprog doesn't treat this right: it didn't accept a list with one tuple
  241. # in it
  242. try:
  243. if isinstance(bounds, str):
  244. raise TypeError
  245. if bounds is None or len(bounds) == 0:
  246. bounds = [(0, None)] * n_x
  247. elif len(bounds) == 1:
  248. b = bounds[0]
  249. if len(b) != 2:
  250. raise ValueError(
  251. "Invalid input for linprog: exactly one lower bound and "
  252. "one upper bound must be specified for each element of x")
  253. bounds = [b] * n_x
  254. elif len(bounds) == n_x:
  255. try:
  256. len(bounds[0])
  257. except BaseException:
  258. bounds = [(bounds[0], bounds[1])] * n_x
  259. for i, b in enumerate(bounds):
  260. if len(b) != 2:
  261. raise ValueError(
  262. "Invalid input for linprog, bound " +
  263. str(i) +
  264. " " +
  265. str(b) +
  266. ": exactly one lower bound and one upper bound must "
  267. "be specified for each element of x")
  268. elif (len(bounds) == 2 and np.isreal(bounds[0])
  269. and np.isreal(bounds[1])):
  270. bounds = [(bounds[0], bounds[1])] * n_x
  271. else:
  272. raise ValueError(
  273. "Invalid input for linprog: exactly one lower bound and one "
  274. "upper bound must be specified for each element of x")
  275. clean_bounds = [] # also creates a copy so user's object isn't changed
  276. for i, b in enumerate(bounds):
  277. if b[0] is not None and b[1] is not None and b[0] > b[1]:
  278. raise ValueError(
  279. "Invalid input for linprog, bound " +
  280. str(i) +
  281. " " +
  282. str(b) +
  283. ": a lower bound must be less than or equal to the "
  284. "corresponding upper bound")
  285. if b[0] == np.inf:
  286. raise ValueError(
  287. "Invalid input for linprog, bound " +
  288. str(i) +
  289. " " +
  290. str(b) +
  291. ": infinity is not a valid lower bound")
  292. if b[1] == -np.inf:
  293. raise ValueError(
  294. "Invalid input for linprog, bound " +
  295. str(i) +
  296. " " +
  297. str(b) +
  298. ": negative infinity is not a valid upper bound")
  299. lb = float(b[0]) if b[0] is not None and b[0] != -np.inf else None
  300. ub = float(b[1]) if b[1] is not None and b[1] != np.inf else None
  301. clean_bounds.append((lb, ub))
  302. bounds = clean_bounds
  303. except ValueError as e:
  304. if "could not convert string to float" in e.args[0]:
  305. raise TypeError
  306. else:
  307. raise e
  308. except TypeError as e:
  309. print(e)
  310. raise TypeError(
  311. "Invalid input for linprog: bounds must be a sequence of "
  312. "(min,max) pairs, each defining bounds on an element of x ")
  313. return c, A_ub, b_ub, A_eq, b_eq, bounds
  314. def _presolve(c, A_ub, b_ub, A_eq, b_eq, bounds, rr, tol=1e-9):
  315. """
  316. Given inputs for a linear programming problem in preferred format,
  317. presolve the problem: identify trivial infeasibilities, redundancies,
  318. and unboundedness, tighten bounds where possible, and eliminate fixed
  319. variables.
  320. Parameters
  321. ----------
  322. c : 1D array
  323. Coefficients of the linear objective function to be minimized.
  324. A_ub : 2D array, optional
  325. 2D array such that ``A_ub @ x`` gives the values of the upper-bound
  326. inequality constraints at ``x``.
  327. b_ub : 1D array, optional
  328. 1D array of values representing the upper-bound of each inequality
  329. constraint (row) in ``A_ub``.
  330. A_eq : 2D array, optional
  331. 2D array such that ``A_eq @ x`` gives the values of the equality
  332. constraints at ``x``.
  333. b_eq : 1D array, optional
  334. 1D array of values representing the RHS of each equality constraint
  335. (row) in ``A_eq``.
  336. bounds : sequence of tuples
  337. ``(min, max)`` pairs for each element in ``x``, defining
  338. the bounds on that parameter. Use None for each of ``min`` or
  339. ``max`` when there is no bound in that direction.
  340. rr : bool
  341. If ``True`` attempts to eliminate any redundant rows in ``A_eq``.
  342. Set False if ``A_eq`` is known to be of full row rank, or if you are
  343. looking for a potential speedup (at the expense of reliability).
  344. tol : float
  345. The tolerance which determines when a solution is "close enough" to
  346. zero in Phase 1 to be considered a basic feasible solution or close
  347. enough to positive to serve as an optimal solution.
  348. Returns
  349. -------
  350. c : 1D array
  351. Coefficients of the linear objective function to be minimized.
  352. c0 : 1D array
  353. Constant term in objective function due to fixed (and eliminated)
  354. variables.
  355. A_ub : 2D array, optional
  356. 2D array such that ``A_ub @ x`` gives the values of the upper-bound
  357. inequality constraints at ``x``.
  358. b_ub : 1D array, optional
  359. 1D array of values representing the upper-bound of each inequality
  360. constraint (row) in ``A_ub``.
  361. A_eq : 2D array, optional
  362. 2D array such that ``A_eq @ x`` gives the values of the equality
  363. constraints at ``x``.
  364. b_eq : 1D array, optional
  365. 1D array of values representing the RHS of each equality constraint
  366. (row) in ``A_eq``.
  367. bounds : sequence of tuples
  368. ``(min, max)`` pairs for each element in ``x``, defining
  369. the bounds on that parameter. Use None for each of ``min`` or
  370. ``max`` when there is no bound in that direction. Bounds have been
  371. tightened where possible.
  372. x : 1D array
  373. Solution vector (when the solution is trivial and can be determined
  374. in presolve)
  375. undo: list of tuples
  376. (index, value) pairs that record the original index and fixed value
  377. for each variable removed from the problem
  378. complete: bool
  379. Whether the solution is complete (solved or determined to be infeasible
  380. or unbounded in presolve)
  381. status : int
  382. An integer representing the exit status of the optimization::
  383. 0 : Optimization terminated successfully
  384. 1 : Iteration limit reached
  385. 2 : Problem appears to be infeasible
  386. 3 : Problem appears to be unbounded
  387. 4 : Serious numerical difficulties encountered
  388. message : str
  389. A string descriptor of the exit status of the optimization.
  390. References
  391. ----------
  392. .. [5] Andersen, Erling D. "Finding all linearly dependent rows in
  393. large-scale linear programming." Optimization Methods and Software
  394. 6.3 (1995): 219-227.
  395. .. [8] Andersen, Erling D., and Knud D. Andersen. "Presolving in linear
  396. programming." Mathematical Programming 71.2 (1995): 221-245.
  397. """
  398. # ideas from Reference [5] by Andersen and Andersen
  399. # however, unlike the reference, this is performed before converting
  400. # problem to standard form
  401. # There are a few advantages:
  402. # * artificial variables have not been added, so matrices are smaller
  403. # * bounds have not been converted to constraints yet. (It is better to
  404. # do that after presolve because presolve may adjust the simple bounds.)
  405. # There are many improvements that can be made, namely:
  406. # * implement remaining checks from [5]
  407. # * loop presolve until no additional changes are made
  408. # * implement additional efficiency improvements in redundancy removal [2]
  409. undo = [] # record of variables eliminated from problem
  410. # constant term in cost function may be added if variables are eliminated
  411. c0 = 0
  412. complete = False # complete is True if detected infeasible/unbounded
  413. x = np.zeros(c.shape) # this is solution vector if completed in presolve
  414. status = 0 # all OK unless determined otherwise
  415. message = ""
  416. # Standard form for bounds (from _clean_inputs) is list of tuples
  417. # but numpy array is more convenient here
  418. # In retrospect, numpy array should have been the standard
  419. bounds = np.array(bounds)
  420. lb = bounds[:, 0]
  421. ub = bounds[:, 1]
  422. lb[np.equal(lb, None)] = -np.inf
  423. ub[np.equal(ub, None)] = np.inf
  424. bounds = bounds.astype(float)
  425. lb = lb.astype(float)
  426. ub = ub.astype(float)
  427. m_eq, n = A_eq.shape
  428. m_ub, n = A_ub.shape
  429. if (sps.issparse(A_eq)):
  430. A_eq = A_eq.tolil()
  431. A_ub = A_ub.tolil()
  432. def where(A):
  433. return A.nonzero()
  434. vstack = sps.vstack
  435. else:
  436. where = np.where
  437. vstack = np.vstack
  438. # zero row in equality constraints
  439. zero_row = np.array(np.sum(A_eq != 0, axis=1) == 0).flatten()
  440. if np.any(zero_row):
  441. if np.any(
  442. np.logical_and(
  443. zero_row,
  444. np.abs(b_eq) > tol)): # test_zero_row_1
  445. # infeasible if RHS is not zero
  446. status = 2
  447. message = ("The problem is (trivially) infeasible due to a row "
  448. "of zeros in the equality constraint matrix with a "
  449. "nonzero corresponding constraint value.")
  450. complete = True
  451. return (c, c0, A_ub, b_ub, A_eq, b_eq, bounds,
  452. x, undo, complete, status, message)
  453. else: # test_zero_row_2
  454. # if RHS is zero, we can eliminate this equation entirely
  455. A_eq = A_eq[np.logical_not(zero_row), :]
  456. b_eq = b_eq[np.logical_not(zero_row)]
  457. # zero row in inequality constraints
  458. zero_row = np.array(np.sum(A_ub != 0, axis=1) == 0).flatten()
  459. if np.any(zero_row):
  460. if np.any(np.logical_and(zero_row, b_ub < -tol)): # test_zero_row_1
  461. # infeasible if RHS is less than zero (because LHS is zero)
  462. status = 2
  463. message = ("The problem is (trivially) infeasible due to a row "
  464. "of zeros in the equality constraint matrix with a "
  465. "nonzero corresponding constraint value.")
  466. complete = True
  467. return (c, c0, A_ub, b_ub, A_eq, b_eq, bounds,
  468. x, undo, complete, status, message)
  469. else: # test_zero_row_2
  470. # if LHS is >= 0, we can eliminate this constraint entirely
  471. A_ub = A_ub[np.logical_not(zero_row), :]
  472. b_ub = b_ub[np.logical_not(zero_row)]
  473. # zero column in (both) constraints
  474. # this indicates that a variable isn't constrained and can be removed
  475. A = vstack((A_eq, A_ub))
  476. if A.shape[0] > 0:
  477. zero_col = np.array(np.sum(A != 0, axis=0) == 0).flatten()
  478. # variable will be at upper or lower bound, depending on objective
  479. x[np.logical_and(zero_col, c < 0)] = ub[
  480. np.logical_and(zero_col, c < 0)]
  481. x[np.logical_and(zero_col, c > 0)] = lb[
  482. np.logical_and(zero_col, c > 0)]
  483. if np.any(np.isinf(x)): # if an unconstrained variable has no bound
  484. status = 3
  485. message = ("If feasible, the problem is (trivially) unbounded "
  486. "due to a zero column in the constraint matrices. If "
  487. "you wish to check whether the problem is infeasible, "
  488. "turn presolve off.")
  489. complete = True
  490. return (c, c0, A_ub, b_ub, A_eq, b_eq, bounds,
  491. x, undo, complete, status, message)
  492. # variables will equal upper/lower bounds will be removed later
  493. lb[np.logical_and(zero_col, c < 0)] = ub[
  494. np.logical_and(zero_col, c < 0)]
  495. ub[np.logical_and(zero_col, c > 0)] = lb[
  496. np.logical_and(zero_col, c > 0)]
  497. # row singleton in equality constraints
  498. # this fixes a variable and removes the constraint
  499. singleton_row = np.array(np.sum(A_eq != 0, axis=1) == 1).flatten()
  500. rows = where(singleton_row)[0]
  501. cols = where(A_eq[rows, :])[1]
  502. if len(rows) > 0:
  503. for row, col in zip(rows, cols):
  504. val = b_eq[row] / A_eq[row, col]
  505. if not lb[col] - tol <= val <= ub[col] + tol:
  506. # infeasible if fixed value is not within bounds
  507. status = 2
  508. message = ("The problem is (trivially) infeasible because a "
  509. "singleton row in the equality constraints is "
  510. "inconsistent with the bounds.")
  511. complete = True
  512. return (c, c0, A_ub, b_ub, A_eq, b_eq, bounds,
  513. x, undo, complete, status, message)
  514. else:
  515. # sets upper and lower bounds at that fixed value - variable
  516. # will be removed later
  517. lb[col] = val
  518. ub[col] = val
  519. A_eq = A_eq[np.logical_not(singleton_row), :]
  520. b_eq = b_eq[np.logical_not(singleton_row)]
  521. # row singleton in inequality constraints
  522. # this indicates a simple bound and the constraint can be removed
  523. # simple bounds may be adjusted here
  524. # After all of the simple bound information is combined here, get_Abc will
  525. # turn the simple bounds into constraints
  526. singleton_row = np.array(np.sum(A_ub != 0, axis=1) == 1).flatten()
  527. cols = where(A_ub[singleton_row, :])[1]
  528. rows = where(singleton_row)[0]
  529. if len(rows) > 0:
  530. for row, col in zip(rows, cols):
  531. val = b_ub[row] / A_ub[row, col]
  532. if A_ub[row, col] > 0: # upper bound
  533. if val < lb[col] - tol: # infeasible
  534. complete = True
  535. elif val < ub[col]: # new upper bound
  536. ub[col] = val
  537. else: # lower bound
  538. if val > ub[col] + tol: # infeasible
  539. complete = True
  540. elif val > lb[col]: # new lower bound
  541. lb[col] = val
  542. if complete:
  543. status = 2
  544. message = ("The problem is (trivially) infeasible because a "
  545. "singleton row in the upper bound constraints is "
  546. "inconsistent with the bounds.")
  547. return (c, c0, A_ub, b_ub, A_eq, b_eq, bounds,
  548. x, undo, complete, status, message)
  549. A_ub = A_ub[np.logical_not(singleton_row), :]
  550. b_ub = b_ub[np.logical_not(singleton_row)]
  551. # identical bounds indicate that variable can be removed
  552. i_f = np.abs(lb - ub) < tol # indices of "fixed" variables
  553. i_nf = np.logical_not(i_f) # indices of "not fixed" variables
  554. # test_bounds_equal_but_infeasible
  555. if np.all(i_f): # if bounds define solution, check for consistency
  556. residual = b_eq - A_eq.dot(lb)
  557. slack = b_ub - A_ub.dot(lb)
  558. if ((A_ub.size > 0 and np.any(slack < 0)) or
  559. (A_eq.size > 0 and not np.allclose(residual, 0))):
  560. status = 2
  561. message = ("The problem is (trivially) infeasible because the "
  562. "bounds fix all variables to values inconsistent with "
  563. "the constraints")
  564. complete = True
  565. return (c, c0, A_ub, b_ub, A_eq, b_eq, bounds,
  566. x, undo, complete, status, message)
  567. ub_mod = ub
  568. lb_mod = lb
  569. if np.any(i_f):
  570. c0 += c[i_f].dot(lb[i_f])
  571. b_eq = b_eq - A_eq[:, i_f].dot(lb[i_f])
  572. b_ub = b_ub - A_ub[:, i_f].dot(lb[i_f])
  573. c = c[i_nf]
  574. x = x[i_nf]
  575. A_eq = A_eq[:, i_nf]
  576. A_ub = A_ub[:, i_nf]
  577. # record of variables to be added back in
  578. undo = [np.nonzero(i_f)[0], lb[i_f]]
  579. # don't remove these entries from bounds; they'll be used later.
  580. # but we _also_ need a version of the bounds with these removed
  581. lb_mod = lb[i_nf]
  582. ub_mod = ub[i_nf]
  583. # no constraints indicates that problem is trivial
  584. if A_eq.size == 0 and A_ub.size == 0:
  585. b_eq = np.array([])
  586. b_ub = np.array([])
  587. # test_empty_constraint_1
  588. if c.size == 0:
  589. status = 0
  590. message = ("The solution was determined in presolve as there are "
  591. "no non-trivial constraints.")
  592. elif (np.any(np.logical_and(c < 0, ub_mod == np.inf)) or
  593. np.any(np.logical_and(c > 0, lb_mod == -np.inf))):
  594. # test_no_constraints()
  595. # test_unbounded_no_nontrivial_constraints_1
  596. # test_unbounded_no_nontrivial_constraints_2
  597. status = 3
  598. message = ("The problem is (trivially) unbounded "
  599. "because there are no non-trivial constraints and "
  600. "a) at least one decision variable is unbounded "
  601. "above and its corresponding cost is negative, or "
  602. "b) at least one decision variable is unbounded below "
  603. "and its corresponding cost is positive. ")
  604. else: # test_empty_constraint_2
  605. status = 0
  606. message = ("The solution was determined in presolve as there are "
  607. "no non-trivial constraints.")
  608. complete = True
  609. x[c < 0] = ub_mod[c < 0]
  610. x[c > 0] = lb_mod[c > 0]
  611. # where c is zero, set x to a finite bound or zero
  612. x_zero_c = ub_mod[c == 0]
  613. x_zero_c[np.isinf(x_zero_c)] = ub_mod[c == 0][np.isinf(x_zero_c)]
  614. x_zero_c[np.isinf(x_zero_c)] = 0
  615. x[c == 0] = x_zero_c
  616. # if this is not the last step of presolve, should convert bounds back
  617. # to array and return here
  618. # *sigh* - convert bounds back to their standard form (list of tuples)
  619. # again, in retrospect, numpy array would be standard form
  620. lb[np.equal(lb, -np.inf)] = None
  621. ub[np.equal(ub, np.inf)] = None
  622. bounds = np.hstack((lb[:, np.newaxis], ub[:, np.newaxis]))
  623. bounds = bounds.tolist()
  624. for i, row in enumerate(bounds):
  625. for j, col in enumerate(row):
  626. if str(
  627. col) == "nan": # comparing col to float("nan") and
  628. # np.nan doesn't work. should use np.isnan
  629. bounds[i][j] = None
  630. # remove redundant (linearly dependent) rows from equality constraints
  631. n_rows_A = A_eq.shape[0]
  632. redundancy_warning = ("A_eq does not appear to be of full row rank. To "
  633. "improve performance, check the problem formulation "
  634. "for redundant equality constraints.")
  635. if (sps.issparse(A_eq)):
  636. if rr and A_eq.size > 0: # TODO: Fast sparse rank check?
  637. A_eq, b_eq, status, message = _remove_redundancy_sparse(A_eq, b_eq)
  638. if A_eq.shape[0] < n_rows_A:
  639. warn(redundancy_warning, OptimizeWarning)
  640. if status != 0:
  641. complete = True
  642. return (c, c0, A_ub, b_ub, A_eq, b_eq, bounds,
  643. x, undo, complete, status, message)
  644. # This is a wild guess for which redundancy removal algorithm will be
  645. # faster. More testing would be good.
  646. small_nullspace = 5
  647. if rr and A_eq.size > 0:
  648. try: # TODO: instead use results of first SVD in _remove_redundancy
  649. rank = np.linalg.matrix_rank(A_eq)
  650. except Exception: # oh well, we'll have to go with _remove_redundancy_dense
  651. rank = 0
  652. if rr and A_eq.size > 0 and rank < A_eq.shape[0]:
  653. warn(redundancy_warning, OptimizeWarning)
  654. dim_row_nullspace = A_eq.shape[0]-rank
  655. if dim_row_nullspace <= small_nullspace:
  656. A_eq, b_eq, status, message = _remove_redundancy(A_eq, b_eq)
  657. if dim_row_nullspace > small_nullspace or status == 4:
  658. A_eq, b_eq, status, message = _remove_redundancy_dense(A_eq, b_eq)
  659. if A_eq.shape[0] < rank:
  660. message = ("Due to numerical issues, redundant equality "
  661. "constraints could not be removed automatically. "
  662. "Try providing your constraint matrices as sparse "
  663. "matrices to activate sparse presolve, try turning "
  664. "off redundancy removal, or try turning off presolve "
  665. "altogether.")
  666. status = 4
  667. if status != 0:
  668. complete = True
  669. return (c, c0, A_ub, b_ub, A_eq, b_eq, bounds,
  670. x, undo, complete, status, message)
  671. def _parse_linprog(c, A_ub, b_ub, A_eq, b_eq, bounds, options):
  672. """
  673. Parse the provided linear programming problem
  674. ``_parse_linprog`` employs two main steps ``_check_sparse_inputs`` and
  675. ``_clean_inputs``. ``_check_sparse_inputs`` checks for sparsity in the
  676. provided constraints (``A_ub`` and ``A_eq) and if these match the provided
  677. sparsity optional values.
  678. ``_clean inputs`` checks of the provided inputs. If no violations are
  679. identified the objective vector, upper bound constraints, equality
  680. constraints, and simple bounds are returned in the expected format.
  681. Parameters
  682. ----------
  683. c : 1D array
  684. Coefficients of the linear objective function to be minimized.
  685. A_ub : 2D array, optional
  686. 2D array such that ``A_ub @ x`` gives the values of the upper-bound
  687. inequality constraints at ``x``.
  688. b_ub : 1D array, optional
  689. 1D array of values representing the upper-bound of each inequality
  690. constraint (row) in ``A_ub``.
  691. A_eq : 2D array, optional
  692. 2D array such that ``A_eq @ x`` gives the values of the equality
  693. constraints at ``x``.
  694. b_eq : 1D array, optional
  695. 1D array of values representing the RHS of each equality constraint
  696. (row) in ``A_eq``.
  697. bounds : sequence
  698. ``(min, max)`` pairs for each element in ``x``, defining
  699. the bounds on that parameter. Use None for one of ``min`` or
  700. ``max`` when there is no bound in that direction. By default
  701. bounds are ``(0, None)`` (non-negative). If a sequence containing a
  702. single tuple is provided, then ``min`` and ``max`` will be applied to
  703. all variables in the problem.
  704. options : dict
  705. A dictionary of solver options. All methods accept the following
  706. generic options:
  707. maxiter : int
  708. Maximum number of iterations to perform.
  709. disp : bool
  710. Set to True to print convergence messages.
  711. For method-specific options, see :func:`show_options('linprog')`.
  712. Returns
  713. -------
  714. c : 1D array
  715. Coefficients of the linear objective function to be minimized.
  716. A_ub : 2D array, optional
  717. 2D array such that ``A_ub @ x`` gives the values of the upper-bound
  718. inequality constraints at ``x``.
  719. b_ub : 1D array, optional
  720. 1D array of values representing the upper-bound of each inequality
  721. constraint (row) in ``A_ub``.
  722. A_eq : 2D array, optional
  723. 2D array such that ``A_eq @ x`` gives the values of the equality
  724. constraints at ``x``.
  725. b_eq : 1D array, optional
  726. 1D array of values representing the RHS of each equality constraint
  727. (row) in ``A_eq``.
  728. bounds : sequence, optional
  729. ``(min, max)`` pairs for each element in ``x``, defining
  730. the bounds on that parameter. Use None for one of ``min`` or
  731. ``max`` when there is no bound in that direction. By default
  732. bounds are ``(0, None)`` (non-negative).
  733. If a sequence containing a single tuple is provided, then ``min`` and
  734. ``max`` will be applied to all variables in the problem.
  735. options : dict, optional
  736. A dictionary of solver options. All methods accept the following
  737. generic options:
  738. maxiter : int
  739. Maximum number of iterations to perform.
  740. disp : bool
  741. Set to True to print convergence messages.
  742. For method-specific options, see :func:`show_options('linprog')`.
  743. """
  744. if options is None:
  745. options = {}
  746. solver_options = {k: v for k, v in options.items()}
  747. solver_options, A_ub, A_eq = _check_sparse_inputs(solver_options, A_ub, A_eq)
  748. # Convert lists to numpy arrays, etc...
  749. c, A_ub, b_ub, A_eq, b_eq, bounds = _clean_inputs(
  750. c, A_ub, b_ub, A_eq, b_eq, bounds)
  751. return c, A_ub, b_ub, A_eq, b_eq, bounds, solver_options
  752. def _get_Abc(c, c0=0, A_ub=None, b_ub=None, A_eq=None, b_eq=None, bounds=None,
  753. undo=[]):
  754. """
  755. Given a linear programming problem of the form:
  756. Minimize::
  757. c @ x
  758. Subject to::
  759. A_ub @ x <= b_ub
  760. A_eq @ x == b_eq
  761. lb <= x <= ub
  762. where ``lb = 0`` and ``ub = None`` unless set in ``bounds``.
  763. Return the problem in standard form:
  764. Minimize::
  765. c @ x
  766. Subject to::
  767. A @ x == b
  768. x >= 0
  769. by adding slack variables and making variable substitutions as necessary.
  770. Parameters
  771. ----------
  772. c : 1D array
  773. Coefficients of the linear objective function to be minimized.
  774. Components corresponding with fixed variables have been eliminated.
  775. c0 : float
  776. Constant term in objective function due to fixed (and eliminated)
  777. variables.
  778. A_ub : 2D array, optional
  779. 2D array such that ``A_ub @ x`` gives the values of the upper-bound
  780. inequality constraints at ``x``.
  781. b_ub : 1D array, optional
  782. 1D array of values representing the upper-bound of each inequality
  783. constraint (row) in ``A_ub``.
  784. A_eq : 2D array, optional
  785. 2D array such that ``A_eq @ x`` gives the values of the equality
  786. constraints at ``x``.
  787. b_eq : 1D array, optional
  788. 1D array of values representing the RHS of each equality constraint
  789. (row) in ``A_eq``.
  790. bounds : sequence of tuples
  791. ``(min, max)`` pairs for each element in ``x``, defining
  792. the bounds on that parameter. Use None for each of ``min`` or
  793. ``max`` when there is no bound in that direction. Bounds have been
  794. tightened where possible.
  795. undo: list of tuples
  796. (`index`, `value`) pairs that record the original index and fixed value
  797. for each variable removed from the problem
  798. Returns
  799. -------
  800. A : 2D array
  801. 2D array such that ``A`` @ ``x``, gives the values of the equality
  802. constraints at ``x``.
  803. b : 1D array
  804. 1D array of values representing the RHS of each equality constraint
  805. (row) in A (for standard form problem).
  806. c : 1D array
  807. Coefficients of the linear objective function to be minimized (for
  808. standard form problem).
  809. c0 : float
  810. Constant term in objective function due to fixed (and eliminated)
  811. variables.
  812. References
  813. ----------
  814. .. [9] Bertsimas, Dimitris, and J. Tsitsiklis. "Introduction to linear
  815. programming." Athena Scientific 1 (1997): 997.
  816. """
  817. if sps.issparse(A_eq):
  818. sparse = True
  819. A_eq = sps.lil_matrix(A_eq)
  820. A_ub = sps.lil_matrix(A_ub)
  821. def hstack(blocks):
  822. return sps.hstack(blocks, format="lil")
  823. def vstack(blocks):
  824. return sps.vstack(blocks, format="lil")
  825. zeros = sps.lil_matrix
  826. eye = sps.eye
  827. else:
  828. sparse = False
  829. hstack = np.hstack
  830. vstack = np.vstack
  831. zeros = np.zeros
  832. eye = np.eye
  833. fixed_x = set()
  834. if len(undo) > 0:
  835. # these are indices of variables removed from the problem
  836. # however, their bounds are still part of the bounds list
  837. fixed_x = set(undo[0])
  838. # they are needed elsewhere, but not here
  839. bounds = [bounds[i] for i in range(len(bounds)) if i not in fixed_x]
  840. # in retrospect, the standard form of bounds should have been an n x 2
  841. # array. maybe change it someday.
  842. # modify problem such that all variables have only non-negativity bounds
  843. bounds = np.array(bounds)
  844. lbs = bounds[:, 0]
  845. ubs = bounds[:, 1]
  846. m_ub, n_ub = A_ub.shape
  847. lb_none = np.equal(lbs, None)
  848. ub_none = np.equal(ubs, None)
  849. lb_some = np.logical_not(lb_none)
  850. ub_some = np.logical_not(ub_none)
  851. # if preprocessing is on, lb == ub can't happen
  852. # if preprocessing is off, then it would be best to convert that
  853. # to an equality constraint, but it's tricky to make the other
  854. # required modifications from inside here.
  855. # unbounded below: substitute xi = -xi' (unbounded above)
  856. l_nolb_someub = np.logical_and(lb_none, ub_some)
  857. i_nolb = np.nonzero(l_nolb_someub)[0]
  858. lbs[l_nolb_someub], ubs[l_nolb_someub] = (
  859. -ubs[l_nolb_someub], lbs[l_nolb_someub])
  860. lb_none = np.equal(lbs, None)
  861. ub_none = np.equal(ubs, None)
  862. lb_some = np.logical_not(lb_none)
  863. ub_some = np.logical_not(ub_none)
  864. c[i_nolb] *= -1
  865. if len(i_nolb) > 0:
  866. if A_ub.shape[0] > 0: # sometimes needed for sparse arrays... weird
  867. A_ub[:, i_nolb] *= -1
  868. if A_eq.shape[0] > 0:
  869. A_eq[:, i_nolb] *= -1
  870. # upper bound: add inequality constraint
  871. i_newub = np.nonzero(ub_some)[0]
  872. ub_newub = ubs[ub_some]
  873. n_bounds = np.count_nonzero(ub_some)
  874. A_ub = vstack((A_ub, zeros((n_bounds, A_ub.shape[1]))))
  875. b_ub = np.concatenate((b_ub, np.zeros(n_bounds)))
  876. A_ub[range(m_ub, A_ub.shape[0]), i_newub] = 1
  877. b_ub[m_ub:] = ub_newub
  878. A1 = vstack((A_ub, A_eq))
  879. b = np.concatenate((b_ub, b_eq))
  880. c = np.concatenate((c, np.zeros((A_ub.shape[0],))))
  881. # unbounded: substitute xi = xi+ + xi-
  882. l_free = np.logical_and(lb_none, ub_none)
  883. i_free = np.nonzero(l_free)[0]
  884. n_free = len(i_free)
  885. A1 = hstack((A1, zeros((A1.shape[0], n_free))))
  886. c = np.concatenate((c, np.zeros(n_free)))
  887. A1[:, range(n_ub, A1.shape[1])] = -A1[:, i_free]
  888. c[np.arange(n_ub, A1.shape[1])] = -c[i_free]
  889. # add slack variables
  890. A2 = vstack([eye(A_ub.shape[0]), zeros((A_eq.shape[0], A_ub.shape[0]))])
  891. A = hstack([A1, A2])
  892. # lower bound: substitute xi = xi' + lb
  893. # now there is a constant term in objective
  894. i_shift = np.nonzero(lb_some)[0]
  895. lb_shift = lbs[lb_some].astype(float)
  896. c0 += np.sum(lb_shift * c[i_shift])
  897. if sparse:
  898. b = b.reshape(-1, 1)
  899. A = A.tocsc()
  900. b -= (A[:, i_shift] * sps.diags(lb_shift)).sum(axis=1)
  901. b = b.ravel()
  902. else:
  903. b -= (A[:, i_shift] * lb_shift).sum(axis=1)
  904. return A, b, c, c0
  905. def _display_summary(message, status, fun, iteration):
  906. """
  907. Print the termination summary of the linear program
  908. Parameters
  909. ----------
  910. message : str
  911. A string descriptor of the exit status of the optimization.
  912. status : int
  913. An integer representing the exit status of the optimization::
  914. 0 : Optimization terminated successfully
  915. 1 : Iteration limit reached
  916. 2 : Problem appears to be infeasible
  917. 3 : Problem appears to be unbounded
  918. 4 : Serious numerical difficulties encountered
  919. fun : float
  920. Value of the objective function.
  921. iteration : iteration
  922. The number of iterations performed.
  923. """
  924. print(message)
  925. if status in (0, 1):
  926. print(" Current function value: {0: <12.6f}".format(fun))
  927. print(" Iterations: {0:d}".format(iteration))
  928. def _postsolve(x, c, A_ub=None, b_ub=None, A_eq=None, b_eq=None, bounds=None,
  929. complete=False, undo=[], tol=1e-8):
  930. """
  931. Given solution x to presolved, standard form linear program x, add
  932. fixed variables back into the problem and undo the variable substitutions
  933. to get solution to original linear program. Also, calculate the objective
  934. function value, slack in original upper bound constraints, and residuals
  935. in original equality constraints.
  936. Parameters
  937. ----------
  938. x : 1D array
  939. Solution vector to the standard-form problem.
  940. c : 1D array
  941. Original coefficients of the linear objective function to be minimized.
  942. A_ub : 2D array, optional
  943. 2D array such that ``A_ub @ x`` gives the values of the upper-bound
  944. inequality constraints at ``x``.
  945. b_ub : 1D array, optional
  946. 1D array of values representing the upper-bound of each inequality
  947. constraint (row) in ``A_ub``.
  948. A_eq : 2D array, optional
  949. 2D array such that ``A_eq @ x`` gives the values of the equality
  950. constraints at ``x``.
  951. b_eq : 1D array, optional
  952. 1D array of values representing the RHS of each equality constraint
  953. (row) in ``A_eq``.
  954. bounds : sequence of tuples
  955. Bounds, as modified in presolve
  956. complete : bool
  957. Whether the solution is was determined in presolve (``True`` if so)
  958. undo: list of tuples
  959. (`index`, `value`) pairs that record the original index and fixed value
  960. for each variable removed from the problem
  961. tol : float
  962. Termination tolerance; see [1]_ Section 4.5.
  963. Returns
  964. -------
  965. x : 1D array
  966. Solution vector to original linear programming problem
  967. fun: float
  968. optimal objective value for original problem
  969. slack : 1D array
  970. The (non-negative) slack in the upper bound constraints, that is,
  971. ``b_ub - A_ub @ x``
  972. con : 1D array
  973. The (nominally zero) residuals of the equality constraints, that is,
  974. ``b - A_eq @ x``
  975. lb : 1D array
  976. The lower bound constraints on the original variables
  977. ub: 1D array
  978. The upper bound constraints on the original variables
  979. """
  980. # note that all the inputs are the ORIGINAL, unmodified versions
  981. # no rows, columns have been removed
  982. # the only exception is bounds; it has been modified
  983. # we need these modified values to undo the variable substitutions
  984. # in retrospect, perhaps this could have been simplified if the "undo"
  985. # variable also contained information for undoing variable substitutions
  986. n_x = len(c)
  987. # we don't have to undo variable substitutions for fixed variables that
  988. # were removed from the problem
  989. no_adjust = set()
  990. # if there were variables removed from the problem, add them back into the
  991. # solution vector
  992. if len(undo) > 0:
  993. no_adjust = set(undo[0])
  994. x = x.tolist()
  995. for i, val in zip(undo[0], undo[1]):
  996. x.insert(i, val)
  997. x = np.array(x)
  998. # now undo variable substitutions
  999. # if "complete", problem was solved in presolve; don't do anything here
  1000. if not complete and bounds is not None: # bounds are never none, probably
  1001. n_unbounded = 0
  1002. for i, b in enumerate(bounds):
  1003. if i in no_adjust:
  1004. continue
  1005. lb, ub = b
  1006. if lb is None and ub is None:
  1007. n_unbounded += 1
  1008. x[i] = x[i] - x[n_x + n_unbounded - 1]
  1009. else:
  1010. if lb is None:
  1011. x[i] = ub - x[i]
  1012. else:
  1013. x[i] += lb
  1014. n_x = len(c)
  1015. x = x[:n_x] # all the rest of the variables were artificial
  1016. fun = x.dot(c)
  1017. slack = b_ub - A_ub.dot(x) # report slack for ORIGINAL UB constraints
  1018. # report residuals of ORIGINAL EQ constraints
  1019. con = b_eq - A_eq.dot(x)
  1020. # Patch for bug #8664. Detecting this sort of issue earlier
  1021. # (via abnormalities in the indicators) would be better.
  1022. bounds = np.array(bounds) # again, this should have been the standard form
  1023. lb = bounds[:, 0]
  1024. ub = bounds[:, 1]
  1025. lb[np.equal(lb, None)] = -np.inf
  1026. ub[np.equal(ub, None)] = np.inf
  1027. return x, fun, slack, con, lb, ub
  1028. def _check_result(x, fun, status, slack, con, lb, ub, tol, message):
  1029. """
  1030. Check the validity of the provided solution.
  1031. A valid (optimal) solution satisfies all bounds, all slack variables are
  1032. negative and all equality constraint residuals are strictly non-zero.
  1033. Further, the lower-bounds, upper-bounds, slack and residuals contain
  1034. no nan values.
  1035. Parameters
  1036. ----------
  1037. x : 1D array
  1038. Solution vector to original linear programming problem
  1039. fun: float
  1040. optimal objective value for original problem
  1041. status : int
  1042. An integer representing the exit status of the optimization::
  1043. 0 : Optimization terminated successfully
  1044. 1 : Iteration limit reached
  1045. 2 : Problem appears to be infeasible
  1046. 3 : Problem appears to be unbounded
  1047. 4 : Serious numerical difficulties encountered
  1048. slack : 1D array
  1049. The (non-negative) slack in the upper bound constraints, that is,
  1050. ``b_ub - A_ub @ x``
  1051. con : 1D array
  1052. The (nominally zero) residuals of the equality constraints, that is,
  1053. ``b - A_eq @ x``
  1054. lb : 1D array
  1055. The lower bound constraints on the original variables
  1056. ub: 1D array
  1057. The upper bound constraints on the original variables
  1058. message : str
  1059. A string descriptor of the exit status of the optimization.
  1060. tol : float
  1061. Termination tolerance; see [1]_ Section 4.5.
  1062. Returns
  1063. -------
  1064. status : int
  1065. An integer representing the exit status of the optimization::
  1066. 0 : Optimization terminated successfully
  1067. 1 : Iteration limit reached
  1068. 2 : Problem appears to be infeasible
  1069. 3 : Problem appears to be unbounded
  1070. 4 : Serious numerical difficulties encountered
  1071. message : str
  1072. A string descriptor of the exit status of the optimization.
  1073. """
  1074. # Somewhat arbitrary, but status 5 is very unusual
  1075. tol = np.sqrt(tol) * 10
  1076. contains_nans = (
  1077. np.isnan(x).any()
  1078. or np.isnan(fun)
  1079. or np.isnan(slack).any()
  1080. or np.isnan(con).any()
  1081. )
  1082. if contains_nans:
  1083. is_feasible = False
  1084. else:
  1085. invalid_bounds = (x < lb - tol).any() or (x > ub + tol).any()
  1086. invalid_slack = status != 3 and (slack < -tol).any()
  1087. invalid_con = status != 3 and (np.abs(con) > tol).any()
  1088. is_feasible = not (invalid_bounds or invalid_slack or invalid_con)
  1089. if status == 0 and not is_feasible:
  1090. status = 4
  1091. message = ("The solution does not satisfy the constraints, yet "
  1092. "no errors were raised and there is no certificate of "
  1093. "infeasibility or unboundedness. This is known to occur "
  1094. "if the `presolve` option is False and the problem is "
  1095. "infeasible. If you encounter this under different "
  1096. "circumstances, please submit a bug report. Otherwise, "
  1097. "please enable presolve.")
  1098. elif status == 0 and contains_nans:
  1099. status = 4
  1100. message = ("Numerical difficulties were encountered but no errors "
  1101. "were raised. This is known to occur if the 'presolve' "
  1102. "option is False, 'sparse' is True, and A_eq includes "
  1103. "redundant rows. If you encounter this under different "
  1104. "circumstances, please submit a bug report. Otherwise, "
  1105. "remove linearly dependent equations from your equality "
  1106. "constraints or enable presolve.")
  1107. elif status == 2 and is_feasible:
  1108. # Occurs if the simplex method exits after phase one with a very
  1109. # nearly basic feasible solution. Postsolving can make the solution
  1110. #basic, however, this solution is NOT optimal
  1111. raise ValueError(message)
  1112. return status, message
  1113. def _postprocess(x, c, A_ub=None, b_ub=None, A_eq=None, b_eq=None, bounds=None,
  1114. complete=False, undo=[], status=0, message="", tol=1e-8,
  1115. iteration=None, disp=False):
  1116. """
  1117. Given solution x to presolved, standard form linear program x, add
  1118. fixed variables back into the problem and undo the variable substitutions
  1119. to get solution to original linear program. Also, calculate the objective
  1120. function value, slack in original upper bound constraints, and residuals
  1121. in original equality constraints.
  1122. Parameters
  1123. ----------
  1124. x : 1D array
  1125. Solution vector to the standard-form problem.
  1126. c : 1D array
  1127. Original coefficients of the linear objective function to be minimized.
  1128. A_ub : 2D array, optional
  1129. 2D array such that ``A_ub @ x`` gives the values of the upper-bound
  1130. inequality constraints at ``x``.
  1131. b_ub : 1D array, optional
  1132. 1D array of values representing the upper-bound of each inequality
  1133. constraint (row) in ``A_ub``.
  1134. A_eq : 2D array, optional
  1135. 2D array such that ``A_eq @ x`` gives the values of the equality
  1136. constraints at ``x``.
  1137. b_eq : 1D array, optional
  1138. 1D array of values representing the RHS of each equality constraint
  1139. (row) in ``A_eq``.
  1140. bounds : sequence of tuples
  1141. Bounds, as modified in presolve
  1142. complete : bool
  1143. Whether the solution is was determined in presolve (``True`` if so)
  1144. undo: list of tuples
  1145. (`index`, `value`) pairs that record the original index and fixed value
  1146. for each variable removed from the problem
  1147. status : int
  1148. An integer representing the exit status of the optimization::
  1149. 0 : Optimization terminated successfully
  1150. 1 : Iteration limit reached
  1151. 2 : Problem appears to be infeasible
  1152. 3 : Problem appears to be unbounded
  1153. 4 : Serious numerical difficulties encountered
  1154. message : str
  1155. A string descriptor of the exit status of the optimization.
  1156. tol : float
  1157. Termination tolerance; see [1]_ Section 4.5.
  1158. Returns
  1159. -------
  1160. x : 1D array
  1161. Solution vector to original linear programming problem
  1162. fun: float
  1163. optimal objective value for original problem
  1164. slack : 1D array
  1165. The (non-negative) slack in the upper bound constraints, that is,
  1166. ``b_ub - A_ub @ x``
  1167. con : 1D array
  1168. The (nominally zero) residuals of the equality constraints, that is,
  1169. ``b - A_eq @ x``
  1170. status : int
  1171. An integer representing the exit status of the optimization::
  1172. 0 : Optimization terminated successfully
  1173. 1 : Iteration limit reached
  1174. 2 : Problem appears to be infeasible
  1175. 3 : Problem appears to be unbounded
  1176. 4 : Serious numerical difficulties encountered
  1177. message : str
  1178. A string descriptor of the exit status of the optimization.
  1179. """
  1180. x, fun, slack, con, lb, ub = _postsolve(
  1181. x, c, A_ub, b_ub, A_eq, b_eq,
  1182. bounds, complete, undo, tol
  1183. )
  1184. status, message = _check_result(
  1185. x, fun, status, slack, con,
  1186. lb, ub, tol, message
  1187. )
  1188. if disp:
  1189. _display_summary(message, status, fun, iteration)
  1190. return x, fun, slack, con, status, message