_linprog_util.py 61 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515
  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_svd, _remove_redundancy_pivot_sparse,
  10. _remove_redundancy_pivot_dense, _remove_redundancy_id
  11. )
  12. from collections import namedtuple
  13. _LPProblem = namedtuple('_LPProblem',
  14. 'c A_ub b_ub A_eq b_eq bounds x0 integrality')
  15. _LPProblem.__new__.__defaults__ = (None,) * 7 # make c the only required arg
  16. _LPProblem.__doc__ = \
  17. """ Represents a linear-programming problem.
  18. Attributes
  19. ----------
  20. c : 1D array
  21. The coefficients of the linear objective function to be minimized.
  22. A_ub : 2D array, optional
  23. The inequality constraint matrix. Each row of ``A_ub`` specifies the
  24. coefficients of a linear inequality constraint on ``x``.
  25. b_ub : 1D array, optional
  26. The inequality constraint vector. Each element represents an
  27. upper bound on the corresponding value of ``A_ub @ x``.
  28. A_eq : 2D array, optional
  29. The equality constraint matrix. Each row of ``A_eq`` specifies the
  30. coefficients of a linear equality constraint on ``x``.
  31. b_eq : 1D array, optional
  32. The equality constraint vector. Each element of ``A_eq @ x`` must equal
  33. the corresponding element of ``b_eq``.
  34. bounds : various valid formats, optional
  35. The bounds of ``x``, as ``min`` and ``max`` pairs.
  36. If bounds are specified for all N variables separately, valid formats
  37. are:
  38. * a 2D array (N x 2);
  39. * a sequence of N sequences, each with 2 values.
  40. If all variables have the same bounds, the bounds can be specified as
  41. a 1-D or 2-D array or sequence with 2 scalar values.
  42. If all variables have a lower bound of 0 and no upper bound, the bounds
  43. parameter can be omitted (or given as None).
  44. Absent lower and/or upper bounds can be specified as -numpy.inf (no
  45. lower bound), numpy.inf (no upper bound) or None (both).
  46. x0 : 1D array, optional
  47. Guess values of the decision variables, which will be refined by
  48. the optimization algorithm. This argument is currently used only by the
  49. 'revised simplex' method, and can only be used if `x0` represents a
  50. basic feasible solution.
  51. integrality : 1-D array or int, optional
  52. Indicates the type of integrality constraint on each decision variable.
  53. ``0`` : Continuous variable; no integrality constraint.
  54. ``1`` : Integer variable; decision variable must be an integer
  55. within `bounds`.
  56. ``2`` : Semi-continuous variable; decision variable must be within
  57. `bounds` or take value ``0``.
  58. ``3`` : Semi-integer variable; decision variable must be an integer
  59. within `bounds` or take value ``0``.
  60. By default, all variables are continuous.
  61. For mixed integrality constraints, supply an array of shape `c.shape`.
  62. To infer a constraint on each decision variable from shorter inputs,
  63. the argument will be broadcasted to `c.shape` using `np.broadcast_to`.
  64. This argument is currently used only by the ``'highs'`` method and
  65. ignored otherwise.
  66. Notes
  67. -----
  68. This namedtuple supports 2 ways of initialization:
  69. >>> lp1 = _LPProblem(c=[-1, 4], A_ub=[[-3, 1], [1, 2]], b_ub=[6, 4])
  70. >>> lp2 = _LPProblem([-1, 4], [[-3, 1], [1, 2]], [6, 4])
  71. Note that only ``c`` is a required argument here, whereas all other arguments
  72. ``A_ub``, ``b_ub``, ``A_eq``, ``b_eq``, ``bounds``, ``x0`` are optional with
  73. default values of None.
  74. For example, ``A_eq`` and ``b_eq`` can be set without ``A_ub`` or ``b_ub``:
  75. >>> lp3 = _LPProblem(c=[-1, 4], A_eq=[[2, 1]], b_eq=[10])
  76. """
  77. def _check_sparse_inputs(options, meth, A_ub, A_eq):
  78. """
  79. Check the provided ``A_ub`` and ``A_eq`` matrices conform to the specified
  80. optional sparsity variables.
  81. Parameters
  82. ----------
  83. A_ub : 2-D array, optional
  84. 2-D array such that ``A_ub @ x`` gives the values of the upper-bound
  85. inequality constraints at ``x``.
  86. A_eq : 2-D array, optional
  87. 2-D array such that ``A_eq @ x`` gives the values of the equality
  88. constraints at ``x``.
  89. options : dict
  90. A dictionary of solver options. All methods accept the following
  91. generic options:
  92. maxiter : int
  93. Maximum number of iterations to perform.
  94. disp : bool
  95. Set to True to print convergence messages.
  96. For method-specific options, see :func:`show_options('linprog')`.
  97. method : str, optional
  98. The algorithm used to solve the standard form problem.
  99. Returns
  100. -------
  101. A_ub : 2-D array, optional
  102. 2-D array such that ``A_ub @ x`` gives the values of the upper-bound
  103. inequality constraints at ``x``.
  104. A_eq : 2-D array, optional
  105. 2-D array such that ``A_eq @ x`` gives the values of the equality
  106. constraints at ``x``.
  107. options : dict
  108. A dictionary of solver options. All methods accept the following
  109. generic options:
  110. maxiter : int
  111. Maximum number of iterations to perform.
  112. disp : bool
  113. Set to True to print convergence messages.
  114. For method-specific options, see :func:`show_options('linprog')`.
  115. """
  116. # This is an undocumented option for unit testing sparse presolve
  117. _sparse_presolve = options.pop('_sparse_presolve', False)
  118. if _sparse_presolve and A_eq is not None:
  119. A_eq = sps.coo_matrix(A_eq)
  120. if _sparse_presolve and A_ub is not None:
  121. A_ub = sps.coo_matrix(A_ub)
  122. sparse_constraint = sps.issparse(A_eq) or sps.issparse(A_ub)
  123. preferred_methods = {"highs", "highs-ds", "highs-ipm"}
  124. dense_methods = {"simplex", "revised simplex"}
  125. if meth in dense_methods and sparse_constraint:
  126. raise ValueError(f"Method '{meth}' does not support sparse "
  127. "constraint matrices. Please consider using one of "
  128. f"{preferred_methods}.")
  129. sparse = options.get('sparse', False)
  130. if not sparse and sparse_constraint and meth == 'interior-point':
  131. options['sparse'] = True
  132. warn("Sparse constraint matrix detected; setting 'sparse':True.",
  133. OptimizeWarning, stacklevel=4)
  134. return options, A_ub, A_eq
  135. def _format_A_constraints(A, n_x, sparse_lhs=False):
  136. """Format the left hand side of the constraints to a 2-D array
  137. Parameters
  138. ----------
  139. A : 2-D array
  140. 2-D array such that ``A @ x`` gives the values of the upper-bound
  141. (in)equality constraints at ``x``.
  142. n_x : int
  143. The number of variables in the linear programming problem.
  144. sparse_lhs : bool
  145. Whether either of `A_ub` or `A_eq` are sparse. If true return a
  146. coo_matrix instead of a numpy array.
  147. Returns
  148. -------
  149. np.ndarray or sparse.coo_matrix
  150. 2-D array such that ``A @ x`` gives the values of the upper-bound
  151. (in)equality constraints at ``x``.
  152. """
  153. if sparse_lhs:
  154. return sps.coo_matrix(
  155. (0, n_x) if A is None else A, dtype=float, copy=True
  156. )
  157. elif A is None:
  158. return np.zeros((0, n_x), dtype=float)
  159. else:
  160. return np.array(A, dtype=float, copy=True)
  161. def _format_b_constraints(b):
  162. """Format the upper bounds of the constraints to a 1-D array
  163. Parameters
  164. ----------
  165. b : 1-D array
  166. 1-D array of values representing the upper-bound of each (in)equality
  167. constraint (row) in ``A``.
  168. Returns
  169. -------
  170. 1-D np.array
  171. 1-D array of values representing the upper-bound of each (in)equality
  172. constraint (row) in ``A``.
  173. """
  174. if b is None:
  175. return np.array([], dtype=float)
  176. b = np.array(b, dtype=float, copy=True).squeeze()
  177. return b if b.size != 1 else b.reshape((-1))
  178. def _clean_inputs(lp):
  179. """
  180. Given user inputs for a linear programming problem, return the
  181. objective vector, upper bound constraints, equality constraints,
  182. and simple bounds in a preferred format.
  183. Parameters
  184. ----------
  185. lp : A `scipy.optimize._linprog_util._LPProblem` consisting of the following fields:
  186. c : 1D array
  187. The coefficients of the linear objective function to be minimized.
  188. A_ub : 2D array, optional
  189. The inequality constraint matrix. Each row of ``A_ub`` specifies the
  190. coefficients of a linear inequality constraint on ``x``.
  191. b_ub : 1D array, optional
  192. The inequality constraint vector. Each element represents an
  193. upper bound on the corresponding value of ``A_ub @ x``.
  194. A_eq : 2D array, optional
  195. The equality constraint matrix. Each row of ``A_eq`` specifies the
  196. coefficients of a linear equality constraint on ``x``.
  197. b_eq : 1D array, optional
  198. The equality constraint vector. Each element of ``A_eq @ x`` must equal
  199. the corresponding element of ``b_eq``.
  200. bounds : various valid formats, optional
  201. The bounds of ``x``, as ``min`` and ``max`` pairs.
  202. If bounds are specified for all N variables separately, valid formats are:
  203. * a 2D array (2 x N or N x 2);
  204. * a sequence of N sequences, each with 2 values.
  205. If all variables have the same bounds, a single pair of values can
  206. be specified. Valid formats are:
  207. * a sequence with 2 scalar values;
  208. * a sequence with a single element containing 2 scalar values.
  209. If all variables have a lower bound of 0 and no upper bound, the bounds
  210. parameter can be omitted (or given as None).
  211. x0 : 1D array, optional
  212. Guess values of the decision variables, which will be refined by
  213. the optimization algorithm. This argument is currently used only by the
  214. 'revised simplex' method, and can only be used if `x0` represents a
  215. basic feasible solution.
  216. Returns
  217. -------
  218. lp : A `scipy.optimize._linprog_util._LPProblem` consisting of the following fields:
  219. c : 1D array
  220. The coefficients of the linear objective function to be minimized.
  221. A_ub : 2D array, optional
  222. The inequality constraint matrix. Each row of ``A_ub`` specifies the
  223. coefficients of a linear inequality constraint on ``x``.
  224. b_ub : 1D array, optional
  225. The inequality constraint vector. Each element represents an
  226. upper bound on the corresponding value of ``A_ub @ x``.
  227. A_eq : 2D array, optional
  228. The equality constraint matrix. Each row of ``A_eq`` specifies the
  229. coefficients of a linear equality constraint on ``x``.
  230. b_eq : 1D array, optional
  231. The equality constraint vector. Each element of ``A_eq @ x`` must equal
  232. the corresponding element of ``b_eq``.
  233. bounds : 2D array
  234. The bounds of ``x``, as ``min`` and ``max`` pairs, one for each of the N
  235. elements of ``x``. The N x 2 array contains lower bounds in the first
  236. column and upper bounds in the 2nd. Unbounded variables have lower
  237. bound -np.inf and/or upper bound np.inf.
  238. x0 : 1D array, optional
  239. Guess values of the decision variables, which will be refined by
  240. the optimization algorithm. This argument is currently used only by the
  241. 'revised simplex' method, and can only be used if `x0` represents a
  242. basic feasible solution.
  243. """
  244. c, A_ub, b_ub, A_eq, b_eq, bounds, x0, integrality = lp
  245. if c is None:
  246. raise TypeError
  247. try:
  248. c = np.array(c, dtype=np.float64, copy=True).squeeze()
  249. except ValueError as e:
  250. raise TypeError(
  251. "Invalid input for linprog: c must be a 1-D array of numerical "
  252. "coefficients") from e
  253. else:
  254. # If c is a single value, convert it to a 1-D array.
  255. if c.size == 1:
  256. c = c.reshape((-1))
  257. n_x = len(c)
  258. if n_x == 0 or len(c.shape) != 1:
  259. raise ValueError(
  260. "Invalid input for linprog: c must be a 1-D array and must "
  261. "not have more than one non-singleton dimension")
  262. if not np.isfinite(c).all():
  263. raise ValueError(
  264. "Invalid input for linprog: c must not contain values "
  265. "inf, nan, or None")
  266. sparse_lhs = sps.issparse(A_eq) or sps.issparse(A_ub)
  267. try:
  268. A_ub = _format_A_constraints(A_ub, n_x, sparse_lhs=sparse_lhs)
  269. except ValueError as e:
  270. raise TypeError(
  271. "Invalid input for linprog: A_ub must be a 2-D array "
  272. "of numerical values") from e
  273. else:
  274. n_ub = A_ub.shape[0]
  275. if len(A_ub.shape) != 2 or A_ub.shape[1] != n_x:
  276. raise ValueError(
  277. "Invalid input for linprog: A_ub must have exactly two "
  278. "dimensions, and the number of columns in A_ub must be "
  279. "equal to the size of c")
  280. if (sps.issparse(A_ub) and not np.isfinite(A_ub.data).all()
  281. or not sps.issparse(A_ub) and not np.isfinite(A_ub).all()):
  282. raise ValueError(
  283. "Invalid input for linprog: A_ub must not contain values "
  284. "inf, nan, or None")
  285. try:
  286. b_ub = _format_b_constraints(b_ub)
  287. except ValueError as e:
  288. raise TypeError(
  289. "Invalid input for linprog: b_ub must be a 1-D array of "
  290. "numerical values, each representing the upper bound of an "
  291. "inequality constraint (row) in A_ub") from e
  292. else:
  293. if b_ub.shape != (n_ub,):
  294. raise ValueError(
  295. "Invalid input for linprog: b_ub must be a 1-D array; b_ub "
  296. "must not have more than one non-singleton dimension and "
  297. "the number of rows in A_ub must equal the number of values "
  298. "in b_ub")
  299. if not np.isfinite(b_ub).all():
  300. raise ValueError(
  301. "Invalid input for linprog: b_ub must not contain values "
  302. "inf, nan, or None")
  303. try:
  304. A_eq = _format_A_constraints(A_eq, n_x, sparse_lhs=sparse_lhs)
  305. except ValueError as e:
  306. raise TypeError(
  307. "Invalid input for linprog: A_eq must be a 2-D array "
  308. "of numerical values") from e
  309. else:
  310. n_eq = A_eq.shape[0]
  311. if len(A_eq.shape) != 2 or A_eq.shape[1] != n_x:
  312. raise ValueError(
  313. "Invalid input for linprog: A_eq must have exactly two "
  314. "dimensions, and the number of columns in A_eq must be "
  315. "equal to the size of c")
  316. if (sps.issparse(A_eq) and not np.isfinite(A_eq.data).all()
  317. or not sps.issparse(A_eq) and not np.isfinite(A_eq).all()):
  318. raise ValueError(
  319. "Invalid input for linprog: A_eq must not contain values "
  320. "inf, nan, or None")
  321. try:
  322. b_eq = _format_b_constraints(b_eq)
  323. except ValueError as e:
  324. raise TypeError(
  325. "Invalid input for linprog: b_eq must be a dense, 1-D array of "
  326. "numerical values, each representing the right hand side of an "
  327. "equality constraint (row) in A_eq") from e
  328. else:
  329. if b_eq.shape != (n_eq,):
  330. raise ValueError(
  331. "Invalid input for linprog: b_eq must be a 1-D array; b_eq "
  332. "must not have more than one non-singleton dimension and "
  333. "the number of rows in A_eq must equal the number of values "
  334. "in b_eq")
  335. if not np.isfinite(b_eq).all():
  336. raise ValueError(
  337. "Invalid input for linprog: b_eq must not contain values "
  338. "inf, nan, or None")
  339. # x0 gives a (optional) starting solution to the solver. If x0 is None,
  340. # skip the checks. Initial solution will be generated automatically.
  341. if x0 is not None:
  342. try:
  343. x0 = np.array(x0, dtype=float, copy=True).squeeze()
  344. except ValueError as e:
  345. raise TypeError(
  346. "Invalid input for linprog: x0 must be a 1-D array of "
  347. "numerical coefficients") from e
  348. if x0.ndim == 0:
  349. x0 = x0.reshape((-1))
  350. if len(x0) == 0 or x0.ndim != 1:
  351. raise ValueError(
  352. "Invalid input for linprog: x0 should be a 1-D array; it "
  353. "must not have more than one non-singleton dimension")
  354. if not x0.size == c.size:
  355. raise ValueError(
  356. "Invalid input for linprog: x0 and c should contain the "
  357. "same number of elements")
  358. if not np.isfinite(x0).all():
  359. raise ValueError(
  360. "Invalid input for linprog: x0 must not contain values "
  361. "inf, nan, or None")
  362. # Bounds can be one of these formats:
  363. # (1) a 2-D array or sequence, with shape N x 2
  364. # (2) a 1-D or 2-D sequence or array with 2 scalars
  365. # (3) None (or an empty sequence or array)
  366. # Unspecified bounds can be represented by None or (-)np.inf.
  367. # All formats are converted into a N x 2 np.array with (-)np.inf where
  368. # bounds are unspecified.
  369. # Prepare clean bounds array
  370. bounds_clean = np.zeros((n_x, 2), dtype=float)
  371. # Convert to a numpy array.
  372. # np.array(..,dtype=float) raises an error if dimensions are inconsistent
  373. # or if there are invalid data types in bounds. Just add a linprog prefix
  374. # to the error and re-raise.
  375. # Creating at least a 2-D array simplifies the cases to distinguish below.
  376. if bounds is None or np.array_equal(bounds, []) or np.array_equal(bounds, [[]]):
  377. bounds = (0, np.inf)
  378. try:
  379. bounds_conv = np.atleast_2d(np.array(bounds, dtype=float))
  380. except ValueError as e:
  381. raise ValueError(
  382. "Invalid input for linprog: unable to interpret bounds, "
  383. "check values and dimensions: " + e.args[0]) from e
  384. except TypeError as e:
  385. raise TypeError(
  386. "Invalid input for linprog: unable to interpret bounds, "
  387. "check values and dimensions: " + e.args[0]) from e
  388. # Check bounds options
  389. bsh = bounds_conv.shape
  390. if len(bsh) > 2:
  391. # Do not try to handle multidimensional bounds input
  392. raise ValueError(
  393. "Invalid input for linprog: provide a 2-D array for bounds, "
  394. "not a {:d}-D array.".format(len(bsh)))
  395. elif np.all(bsh == (n_x, 2)):
  396. # Regular N x 2 array
  397. bounds_clean = bounds_conv
  398. elif (np.all(bsh == (2, 1)) or np.all(bsh == (1, 2))):
  399. # 2 values: interpret as overall lower and upper bound
  400. bounds_flat = bounds_conv.flatten()
  401. bounds_clean[:, 0] = bounds_flat[0]
  402. bounds_clean[:, 1] = bounds_flat[1]
  403. elif np.all(bsh == (2, n_x)):
  404. # Reject a 2 x N array
  405. raise ValueError(
  406. "Invalid input for linprog: provide a {:d} x 2 array for bounds, "
  407. "not a 2 x {:d} array.".format(n_x, n_x))
  408. else:
  409. raise ValueError(
  410. "Invalid input for linprog: unable to interpret bounds with this "
  411. "dimension tuple: {0}.".format(bsh))
  412. # The process above creates nan-s where the input specified None
  413. # Convert the nan-s in the 1st column to -np.inf and in the 2nd column
  414. # to np.inf
  415. i_none = np.isnan(bounds_clean[:, 0])
  416. bounds_clean[i_none, 0] = -np.inf
  417. i_none = np.isnan(bounds_clean[:, 1])
  418. bounds_clean[i_none, 1] = np.inf
  419. return _LPProblem(c, A_ub, b_ub, A_eq, b_eq, bounds_clean, x0, integrality)
  420. def _presolve(lp, rr, rr_method, tol=1e-9):
  421. """
  422. Given inputs for a linear programming problem in preferred format,
  423. presolve the problem: identify trivial infeasibilities, redundancies,
  424. and unboundedness, tighten bounds where possible, and eliminate fixed
  425. variables.
  426. Parameters
  427. ----------
  428. lp : A `scipy.optimize._linprog_util._LPProblem` consisting of the following fields:
  429. c : 1D array
  430. The coefficients of the linear objective function to be minimized.
  431. A_ub : 2D array, optional
  432. The inequality constraint matrix. Each row of ``A_ub`` specifies the
  433. coefficients of a linear inequality constraint on ``x``.
  434. b_ub : 1D array, optional
  435. The inequality constraint vector. Each element represents an
  436. upper bound on the corresponding value of ``A_ub @ x``.
  437. A_eq : 2D array, optional
  438. The equality constraint matrix. Each row of ``A_eq`` specifies the
  439. coefficients of a linear equality constraint on ``x``.
  440. b_eq : 1D array, optional
  441. The equality constraint vector. Each element of ``A_eq @ x`` must equal
  442. the corresponding element of ``b_eq``.
  443. bounds : 2D array
  444. The bounds of ``x``, as ``min`` and ``max`` pairs, one for each of the N
  445. elements of ``x``. The N x 2 array contains lower bounds in the first
  446. column and upper bounds in the 2nd. Unbounded variables have lower
  447. bound -np.inf and/or upper bound np.inf.
  448. x0 : 1D array, optional
  449. Guess values of the decision variables, which will be refined by
  450. the optimization algorithm. This argument is currently used only by the
  451. 'revised simplex' method, and can only be used if `x0` represents a
  452. basic feasible solution.
  453. rr : bool
  454. If ``True`` attempts to eliminate any redundant rows in ``A_eq``.
  455. Set False if ``A_eq`` is known to be of full row rank, or if you are
  456. looking for a potential speedup (at the expense of reliability).
  457. rr_method : string
  458. Method used to identify and remove redundant rows from the
  459. equality constraint matrix after presolve.
  460. tol : float
  461. The tolerance which determines when a solution is "close enough" to
  462. zero in Phase 1 to be considered a basic feasible solution or close
  463. enough to positive to serve as an optimal solution.
  464. Returns
  465. -------
  466. lp : A `scipy.optimize._linprog_util._LPProblem` consisting of the following fields:
  467. c : 1D array
  468. The coefficients of the linear objective function to be minimized.
  469. A_ub : 2D array, optional
  470. The inequality constraint matrix. Each row of ``A_ub`` specifies the
  471. coefficients of a linear inequality constraint on ``x``.
  472. b_ub : 1D array, optional
  473. The inequality constraint vector. Each element represents an
  474. upper bound on the corresponding value of ``A_ub @ x``.
  475. A_eq : 2D array, optional
  476. The equality constraint matrix. Each row of ``A_eq`` specifies the
  477. coefficients of a linear equality constraint on ``x``.
  478. b_eq : 1D array, optional
  479. The equality constraint vector. Each element of ``A_eq @ x`` must equal
  480. the corresponding element of ``b_eq``.
  481. bounds : 2D array
  482. The bounds of ``x``, as ``min`` and ``max`` pairs, possibly tightened.
  483. x0 : 1D array, optional
  484. Guess values of the decision variables, which will be refined by
  485. the optimization algorithm. This argument is currently used only by the
  486. 'revised simplex' method, and can only be used if `x0` represents a
  487. basic feasible solution.
  488. c0 : 1D array
  489. Constant term in objective function due to fixed (and eliminated)
  490. variables.
  491. x : 1D array
  492. Solution vector (when the solution is trivial and can be determined
  493. in presolve)
  494. revstack: list of functions
  495. the functions in the list reverse the operations of _presolve()
  496. the function signature is x_org = f(x_mod), where x_mod is the result
  497. of a presolve step and x_org the value at the start of the step
  498. (currently, the revstack contains only one function)
  499. complete: bool
  500. Whether the solution is complete (solved or determined to be infeasible
  501. or unbounded in presolve)
  502. status : int
  503. An integer representing the exit status of the optimization::
  504. 0 : Optimization terminated successfully
  505. 1 : Iteration limit reached
  506. 2 : Problem appears to be infeasible
  507. 3 : Problem appears to be unbounded
  508. 4 : Serious numerical difficulties encountered
  509. message : str
  510. A string descriptor of the exit status of the optimization.
  511. References
  512. ----------
  513. .. [5] Andersen, Erling D. "Finding all linearly dependent rows in
  514. large-scale linear programming." Optimization Methods and Software
  515. 6.3 (1995): 219-227.
  516. .. [8] Andersen, Erling D., and Knud D. Andersen. "Presolving in linear
  517. programming." Mathematical Programming 71.2 (1995): 221-245.
  518. """
  519. # ideas from Reference [5] by Andersen and Andersen
  520. # however, unlike the reference, this is performed before converting
  521. # problem to standard form
  522. # There are a few advantages:
  523. # * artificial variables have not been added, so matrices are smaller
  524. # * bounds have not been converted to constraints yet. (It is better to
  525. # do that after presolve because presolve may adjust the simple bounds.)
  526. # There are many improvements that can be made, namely:
  527. # * implement remaining checks from [5]
  528. # * loop presolve until no additional changes are made
  529. # * implement additional efficiency improvements in redundancy removal [2]
  530. c, A_ub, b_ub, A_eq, b_eq, bounds, x0, _ = lp
  531. revstack = [] # record of variables eliminated from problem
  532. # constant term in cost function may be added if variables are eliminated
  533. c0 = 0
  534. complete = False # complete is True if detected infeasible/unbounded
  535. x = np.zeros(c.shape) # this is solution vector if completed in presolve
  536. status = 0 # all OK unless determined otherwise
  537. message = ""
  538. # Lower and upper bounds. Copy to prevent feedback.
  539. lb = bounds[:, 0].copy()
  540. ub = bounds[:, 1].copy()
  541. m_eq, n = A_eq.shape
  542. m_ub, n = A_ub.shape
  543. if (rr_method is not None
  544. and rr_method.lower() not in {"svd", "pivot", "id"}):
  545. message = ("'" + str(rr_method) + "' is not a valid option "
  546. "for redundancy removal. Valid options are 'SVD', "
  547. "'pivot', and 'ID'.")
  548. raise ValueError(message)
  549. if sps.issparse(A_eq):
  550. A_eq = A_eq.tocsr()
  551. A_ub = A_ub.tocsr()
  552. def where(A):
  553. return A.nonzero()
  554. vstack = sps.vstack
  555. else:
  556. where = np.where
  557. vstack = np.vstack
  558. # upper bounds > lower bounds
  559. if np.any(ub < lb) or np.any(lb == np.inf) or np.any(ub == -np.inf):
  560. status = 2
  561. message = ("The problem is (trivially) infeasible since one "
  562. "or more upper bounds are smaller than the corresponding "
  563. "lower bounds, a lower bound is np.inf or an upper bound "
  564. "is -np.inf.")
  565. complete = True
  566. return (_LPProblem(c, A_ub, b_ub, A_eq, b_eq, bounds, x0),
  567. c0, x, revstack, complete, status, message)
  568. # zero row in equality constraints
  569. zero_row = np.array(np.sum(A_eq != 0, axis=1) == 0).flatten()
  570. if np.any(zero_row):
  571. if np.any(
  572. np.logical_and(
  573. zero_row,
  574. np.abs(b_eq) > tol)): # test_zero_row_1
  575. # infeasible if RHS is not zero
  576. status = 2
  577. message = ("The problem is (trivially) infeasible due to a row "
  578. "of zeros in the equality constraint matrix with a "
  579. "nonzero corresponding constraint value.")
  580. complete = True
  581. return (_LPProblem(c, A_ub, b_ub, A_eq, b_eq, bounds, x0),
  582. c0, x, revstack, complete, status, message)
  583. else: # test_zero_row_2
  584. # if RHS is zero, we can eliminate this equation entirely
  585. A_eq = A_eq[np.logical_not(zero_row), :]
  586. b_eq = b_eq[np.logical_not(zero_row)]
  587. # zero row in inequality constraints
  588. zero_row = np.array(np.sum(A_ub != 0, axis=1) == 0).flatten()
  589. if np.any(zero_row):
  590. if np.any(np.logical_and(zero_row, b_ub < -tol)): # test_zero_row_1
  591. # infeasible if RHS is less than zero (because LHS is zero)
  592. status = 2
  593. message = ("The problem is (trivially) infeasible due to a row "
  594. "of zeros in the equality constraint matrix with a "
  595. "nonzero corresponding constraint value.")
  596. complete = True
  597. return (_LPProblem(c, A_ub, b_ub, A_eq, b_eq, bounds, x0),
  598. c0, x, revstack, complete, status, message)
  599. else: # test_zero_row_2
  600. # if LHS is >= 0, we can eliminate this constraint entirely
  601. A_ub = A_ub[np.logical_not(zero_row), :]
  602. b_ub = b_ub[np.logical_not(zero_row)]
  603. # zero column in (both) constraints
  604. # this indicates that a variable isn't constrained and can be removed
  605. A = vstack((A_eq, A_ub))
  606. if A.shape[0] > 0:
  607. zero_col = np.array(np.sum(A != 0, axis=0) == 0).flatten()
  608. # variable will be at upper or lower bound, depending on objective
  609. x[np.logical_and(zero_col, c < 0)] = ub[
  610. np.logical_and(zero_col, c < 0)]
  611. x[np.logical_and(zero_col, c > 0)] = lb[
  612. np.logical_and(zero_col, c > 0)]
  613. if np.any(np.isinf(x)): # if an unconstrained variable has no bound
  614. status = 3
  615. message = ("If feasible, the problem is (trivially) unbounded "
  616. "due to a zero column in the constraint matrices. If "
  617. "you wish to check whether the problem is infeasible, "
  618. "turn presolve off.")
  619. complete = True
  620. return (_LPProblem(c, A_ub, b_ub, A_eq, b_eq, bounds, x0),
  621. c0, x, revstack, complete, status, message)
  622. # variables will equal upper/lower bounds will be removed later
  623. lb[np.logical_and(zero_col, c < 0)] = ub[
  624. np.logical_and(zero_col, c < 0)]
  625. ub[np.logical_and(zero_col, c > 0)] = lb[
  626. np.logical_and(zero_col, c > 0)]
  627. # row singleton in equality constraints
  628. # this fixes a variable and removes the constraint
  629. singleton_row = np.array(np.sum(A_eq != 0, axis=1) == 1).flatten()
  630. rows = where(singleton_row)[0]
  631. cols = where(A_eq[rows, :])[1]
  632. if len(rows) > 0:
  633. for row, col in zip(rows, cols):
  634. val = b_eq[row] / A_eq[row, col]
  635. if not lb[col] - tol <= val <= ub[col] + tol:
  636. # infeasible if fixed value is not within bounds
  637. status = 2
  638. message = ("The problem is (trivially) infeasible because a "
  639. "singleton row in the equality constraints is "
  640. "inconsistent with the bounds.")
  641. complete = True
  642. return (_LPProblem(c, A_ub, b_ub, A_eq, b_eq, bounds, x0),
  643. c0, x, revstack, complete, status, message)
  644. else:
  645. # sets upper and lower bounds at that fixed value - variable
  646. # will be removed later
  647. lb[col] = val
  648. ub[col] = val
  649. A_eq = A_eq[np.logical_not(singleton_row), :]
  650. b_eq = b_eq[np.logical_not(singleton_row)]
  651. # row singleton in inequality constraints
  652. # this indicates a simple bound and the constraint can be removed
  653. # simple bounds may be adjusted here
  654. # After all of the simple bound information is combined here, get_Abc will
  655. # turn the simple bounds into constraints
  656. singleton_row = np.array(np.sum(A_ub != 0, axis=1) == 1).flatten()
  657. cols = where(A_ub[singleton_row, :])[1]
  658. rows = where(singleton_row)[0]
  659. if len(rows) > 0:
  660. for row, col in zip(rows, cols):
  661. val = b_ub[row] / A_ub[row, col]
  662. if A_ub[row, col] > 0: # upper bound
  663. if val < lb[col] - tol: # infeasible
  664. complete = True
  665. elif val < ub[col]: # new upper bound
  666. ub[col] = val
  667. else: # lower bound
  668. if val > ub[col] + tol: # infeasible
  669. complete = True
  670. elif val > lb[col]: # new lower bound
  671. lb[col] = val
  672. if complete:
  673. status = 2
  674. message = ("The problem is (trivially) infeasible because a "
  675. "singleton row in the upper bound constraints is "
  676. "inconsistent with the bounds.")
  677. return (_LPProblem(c, A_ub, b_ub, A_eq, b_eq, bounds, x0),
  678. c0, x, revstack, complete, status, message)
  679. A_ub = A_ub[np.logical_not(singleton_row), :]
  680. b_ub = b_ub[np.logical_not(singleton_row)]
  681. # identical bounds indicate that variable can be removed
  682. i_f = np.abs(lb - ub) < tol # indices of "fixed" variables
  683. i_nf = np.logical_not(i_f) # indices of "not fixed" variables
  684. # test_bounds_equal_but_infeasible
  685. if np.all(i_f): # if bounds define solution, check for consistency
  686. residual = b_eq - A_eq.dot(lb)
  687. slack = b_ub - A_ub.dot(lb)
  688. if ((A_ub.size > 0 and np.any(slack < 0)) or
  689. (A_eq.size > 0 and not np.allclose(residual, 0))):
  690. status = 2
  691. message = ("The problem is (trivially) infeasible because the "
  692. "bounds fix all variables to values inconsistent with "
  693. "the constraints")
  694. complete = True
  695. return (_LPProblem(c, A_ub, b_ub, A_eq, b_eq, bounds, x0),
  696. c0, x, revstack, complete, status, message)
  697. ub_mod = ub
  698. lb_mod = lb
  699. if np.any(i_f):
  700. c0 += c[i_f].dot(lb[i_f])
  701. b_eq = b_eq - A_eq[:, i_f].dot(lb[i_f])
  702. b_ub = b_ub - A_ub[:, i_f].dot(lb[i_f])
  703. c = c[i_nf]
  704. x_undo = lb[i_f] # not x[i_f], x is just zeroes
  705. x = x[i_nf]
  706. # user guess x0 stays separate from presolve solution x
  707. if x0 is not None:
  708. x0 = x0[i_nf]
  709. A_eq = A_eq[:, i_nf]
  710. A_ub = A_ub[:, i_nf]
  711. # modify bounds
  712. lb_mod = lb[i_nf]
  713. ub_mod = ub[i_nf]
  714. def rev(x_mod):
  715. # Function to restore x: insert x_undo into x_mod.
  716. # When elements have been removed at positions k1, k2, k3, ...
  717. # then these must be replaced at (after) positions k1-1, k2-2,
  718. # k3-3, ... in the modified array to recreate the original
  719. i = np.flatnonzero(i_f)
  720. # Number of variables to restore
  721. N = len(i)
  722. index_offset = np.arange(N)
  723. # Create insert indices
  724. insert_indices = i - index_offset
  725. x_rev = np.insert(x_mod.astype(float), insert_indices, x_undo)
  726. return x_rev
  727. # Use revstack as a list of functions, currently just this one.
  728. revstack.append(rev)
  729. # no constraints indicates that problem is trivial
  730. if A_eq.size == 0 and A_ub.size == 0:
  731. b_eq = np.array([])
  732. b_ub = np.array([])
  733. # test_empty_constraint_1
  734. if c.size == 0:
  735. status = 0
  736. message = ("The solution was determined in presolve as there are "
  737. "no non-trivial constraints.")
  738. elif (np.any(np.logical_and(c < 0, ub_mod == np.inf)) or
  739. np.any(np.logical_and(c > 0, lb_mod == -np.inf))):
  740. # test_no_constraints()
  741. # test_unbounded_no_nontrivial_constraints_1
  742. # test_unbounded_no_nontrivial_constraints_2
  743. status = 3
  744. message = ("The problem is (trivially) unbounded "
  745. "because there are no non-trivial constraints and "
  746. "a) at least one decision variable is unbounded "
  747. "above and its corresponding cost is negative, or "
  748. "b) at least one decision variable is unbounded below "
  749. "and its corresponding cost is positive. ")
  750. else: # test_empty_constraint_2
  751. status = 0
  752. message = ("The solution was determined in presolve as there are "
  753. "no non-trivial constraints.")
  754. complete = True
  755. x[c < 0] = ub_mod[c < 0]
  756. x[c > 0] = lb_mod[c > 0]
  757. # where c is zero, set x to a finite bound or zero
  758. x_zero_c = ub_mod[c == 0]
  759. x_zero_c[np.isinf(x_zero_c)] = ub_mod[c == 0][np.isinf(x_zero_c)]
  760. x_zero_c[np.isinf(x_zero_c)] = 0
  761. x[c == 0] = x_zero_c
  762. # if this is not the last step of presolve, should convert bounds back
  763. # to array and return here
  764. # Convert modified lb and ub back into N x 2 bounds
  765. bounds = np.hstack((lb_mod[:, np.newaxis], ub_mod[:, np.newaxis]))
  766. # remove redundant (linearly dependent) rows from equality constraints
  767. n_rows_A = A_eq.shape[0]
  768. redundancy_warning = ("A_eq does not appear to be of full row rank. To "
  769. "improve performance, check the problem formulation "
  770. "for redundant equality constraints.")
  771. if (sps.issparse(A_eq)):
  772. if rr and A_eq.size > 0: # TODO: Fast sparse rank check?
  773. rr_res = _remove_redundancy_pivot_sparse(A_eq, b_eq)
  774. A_eq, b_eq, status, message = rr_res
  775. if A_eq.shape[0] < n_rows_A:
  776. warn(redundancy_warning, OptimizeWarning, stacklevel=1)
  777. if status != 0:
  778. complete = True
  779. return (_LPProblem(c, A_ub, b_ub, A_eq, b_eq, bounds, x0),
  780. c0, x, revstack, complete, status, message)
  781. # This is a wild guess for which redundancy removal algorithm will be
  782. # faster. More testing would be good.
  783. small_nullspace = 5
  784. if rr and A_eq.size > 0:
  785. try: # TODO: use results of first SVD in _remove_redundancy_svd
  786. rank = np.linalg.matrix_rank(A_eq)
  787. # oh well, we'll have to go with _remove_redundancy_pivot_dense
  788. except Exception:
  789. rank = 0
  790. if rr and A_eq.size > 0 and rank < A_eq.shape[0]:
  791. warn(redundancy_warning, OptimizeWarning, stacklevel=3)
  792. dim_row_nullspace = A_eq.shape[0]-rank
  793. if rr_method is None:
  794. if dim_row_nullspace <= small_nullspace:
  795. rr_res = _remove_redundancy_svd(A_eq, b_eq)
  796. A_eq, b_eq, status, message = rr_res
  797. if dim_row_nullspace > small_nullspace or status == 4:
  798. rr_res = _remove_redundancy_pivot_dense(A_eq, b_eq)
  799. A_eq, b_eq, status, message = rr_res
  800. else:
  801. rr_method = rr_method.lower()
  802. if rr_method == "svd":
  803. rr_res = _remove_redundancy_svd(A_eq, b_eq)
  804. A_eq, b_eq, status, message = rr_res
  805. elif rr_method == "pivot":
  806. rr_res = _remove_redundancy_pivot_dense(A_eq, b_eq)
  807. A_eq, b_eq, status, message = rr_res
  808. elif rr_method == "id":
  809. rr_res = _remove_redundancy_id(A_eq, b_eq, rank)
  810. A_eq, b_eq, status, message = rr_res
  811. else: # shouldn't get here; option validity checked above
  812. pass
  813. if A_eq.shape[0] < rank:
  814. message = ("Due to numerical issues, redundant equality "
  815. "constraints could not be removed automatically. "
  816. "Try providing your constraint matrices as sparse "
  817. "matrices to activate sparse presolve, try turning "
  818. "off redundancy removal, or try turning off presolve "
  819. "altogether.")
  820. status = 4
  821. if status != 0:
  822. complete = True
  823. return (_LPProblem(c, A_ub, b_ub, A_eq, b_eq, bounds, x0),
  824. c0, x, revstack, complete, status, message)
  825. def _parse_linprog(lp, options, meth):
  826. """
  827. Parse the provided linear programming problem
  828. ``_parse_linprog`` employs two main steps ``_check_sparse_inputs`` and
  829. ``_clean_inputs``. ``_check_sparse_inputs`` checks for sparsity in the
  830. provided constraints (``A_ub`` and ``A_eq) and if these match the provided
  831. sparsity optional values.
  832. ``_clean inputs`` checks of the provided inputs. If no violations are
  833. identified the objective vector, upper bound constraints, equality
  834. constraints, and simple bounds are returned in the expected format.
  835. Parameters
  836. ----------
  837. lp : A `scipy.optimize._linprog_util._LPProblem` consisting of the following fields:
  838. c : 1D array
  839. The coefficients of the linear objective function to be minimized.
  840. A_ub : 2D array, optional
  841. The inequality constraint matrix. Each row of ``A_ub`` specifies the
  842. coefficients of a linear inequality constraint on ``x``.
  843. b_ub : 1D array, optional
  844. The inequality constraint vector. Each element represents an
  845. upper bound on the corresponding value of ``A_ub @ x``.
  846. A_eq : 2D array, optional
  847. The equality constraint matrix. Each row of ``A_eq`` specifies the
  848. coefficients of a linear equality constraint on ``x``.
  849. b_eq : 1D array, optional
  850. The equality constraint vector. Each element of ``A_eq @ x`` must equal
  851. the corresponding element of ``b_eq``.
  852. bounds : various valid formats, optional
  853. The bounds of ``x``, as ``min`` and ``max`` pairs.
  854. If bounds are specified for all N variables separately, valid formats are:
  855. * a 2D array (2 x N or N x 2);
  856. * a sequence of N sequences, each with 2 values.
  857. If all variables have the same bounds, a single pair of values can
  858. be specified. Valid formats are:
  859. * a sequence with 2 scalar values;
  860. * a sequence with a single element containing 2 scalar values.
  861. If all variables have a lower bound of 0 and no upper bound, the bounds
  862. parameter can be omitted (or given as None).
  863. x0 : 1D array, optional
  864. Guess values of the decision variables, which will be refined by
  865. the optimization algorithm. This argument is currently used only by the
  866. 'revised simplex' method, and can only be used if `x0` represents a
  867. basic feasible solution.
  868. options : dict
  869. A dictionary of solver options. All methods accept the following
  870. generic options:
  871. maxiter : int
  872. Maximum number of iterations to perform.
  873. disp : bool
  874. Set to True to print convergence messages.
  875. For method-specific options, see :func:`show_options('linprog')`.
  876. Returns
  877. -------
  878. lp : A `scipy.optimize._linprog_util._LPProblem` consisting of the following fields:
  879. c : 1D array
  880. The coefficients of the linear objective function to be minimized.
  881. A_ub : 2D array, optional
  882. The inequality constraint matrix. Each row of ``A_ub`` specifies the
  883. coefficients of a linear inequality constraint on ``x``.
  884. b_ub : 1D array, optional
  885. The inequality constraint vector. Each element represents an
  886. upper bound on the corresponding value of ``A_ub @ x``.
  887. A_eq : 2D array, optional
  888. The equality constraint matrix. Each row of ``A_eq`` specifies the
  889. coefficients of a linear equality constraint on ``x``.
  890. b_eq : 1D array, optional
  891. The equality constraint vector. Each element of ``A_eq @ x`` must equal
  892. the corresponding element of ``b_eq``.
  893. bounds : 2D array
  894. The bounds of ``x``, as ``min`` and ``max`` pairs, one for each of the N
  895. elements of ``x``. The N x 2 array contains lower bounds in the first
  896. column and upper bounds in the 2nd. Unbounded variables have lower
  897. bound -np.inf and/or upper bound np.inf.
  898. x0 : 1D array, optional
  899. Guess values of the decision variables, which will be refined by
  900. the optimization algorithm. This argument is currently used only by the
  901. 'revised simplex' method, and can only be used if `x0` represents a
  902. basic feasible solution.
  903. options : dict, optional
  904. A dictionary of solver options. All methods accept the following
  905. generic options:
  906. maxiter : int
  907. Maximum number of iterations to perform.
  908. disp : bool
  909. Set to True to print convergence messages.
  910. For method-specific options, see :func:`show_options('linprog')`.
  911. """
  912. if options is None:
  913. options = {}
  914. solver_options = {k: v for k, v in options.items()}
  915. solver_options, A_ub, A_eq = _check_sparse_inputs(solver_options, meth,
  916. lp.A_ub, lp.A_eq)
  917. # Convert lists to numpy arrays, etc...
  918. lp = _clean_inputs(lp._replace(A_ub=A_ub, A_eq=A_eq))
  919. return lp, solver_options
  920. def _get_Abc(lp, c0):
  921. """
  922. Given a linear programming problem of the form:
  923. Minimize::
  924. c @ x
  925. Subject to::
  926. A_ub @ x <= b_ub
  927. A_eq @ x == b_eq
  928. lb <= x <= ub
  929. where ``lb = 0`` and ``ub = None`` unless set in ``bounds``.
  930. Return the problem in standard form:
  931. Minimize::
  932. c @ x
  933. Subject to::
  934. A @ x == b
  935. x >= 0
  936. by adding slack variables and making variable substitutions as necessary.
  937. Parameters
  938. ----------
  939. lp : A `scipy.optimize._linprog_util._LPProblem` consisting of the following fields:
  940. c : 1D array
  941. The coefficients of the linear objective function to be minimized.
  942. A_ub : 2D array, optional
  943. The inequality constraint matrix. Each row of ``A_ub`` specifies the
  944. coefficients of a linear inequality constraint on ``x``.
  945. b_ub : 1D array, optional
  946. The inequality constraint vector. Each element represents an
  947. upper bound on the corresponding value of ``A_ub @ x``.
  948. A_eq : 2D array, optional
  949. The equality constraint matrix. Each row of ``A_eq`` specifies the
  950. coefficients of a linear equality constraint on ``x``.
  951. b_eq : 1D array, optional
  952. The equality constraint vector. Each element of ``A_eq @ x`` must equal
  953. the corresponding element of ``b_eq``.
  954. bounds : 2D array
  955. The bounds of ``x``, lower bounds in the 1st column, upper
  956. bounds in the 2nd column. The bounds are possibly tightened
  957. by the presolve procedure.
  958. x0 : 1D array, optional
  959. Guess values of the decision variables, which will be refined by
  960. the optimization algorithm. This argument is currently used only by the
  961. 'revised simplex' method, and can only be used if `x0` represents a
  962. basic feasible solution.
  963. c0 : float
  964. Constant term in objective function due to fixed (and eliminated)
  965. variables.
  966. Returns
  967. -------
  968. A : 2-D array
  969. 2-D array such that ``A`` @ ``x``, gives the values of the equality
  970. constraints at ``x``.
  971. b : 1-D array
  972. 1-D array of values representing the RHS of each equality constraint
  973. (row) in A (for standard form problem).
  974. c : 1-D array
  975. Coefficients of the linear objective function to be minimized (for
  976. standard form problem).
  977. c0 : float
  978. Constant term in objective function due to fixed (and eliminated)
  979. variables.
  980. x0 : 1-D array
  981. Starting values of the independent variables, which will be refined by
  982. the optimization algorithm
  983. References
  984. ----------
  985. .. [9] Bertsimas, Dimitris, and J. Tsitsiklis. "Introduction to linear
  986. programming." Athena Scientific 1 (1997): 997.
  987. """
  988. c, A_ub, b_ub, A_eq, b_eq, bounds, x0, integrality = lp
  989. if sps.issparse(A_eq):
  990. sparse = True
  991. A_eq = sps.csr_matrix(A_eq)
  992. A_ub = sps.csr_matrix(A_ub)
  993. def hstack(blocks):
  994. return sps.hstack(blocks, format="csr")
  995. def vstack(blocks):
  996. return sps.vstack(blocks, format="csr")
  997. zeros = sps.csr_matrix
  998. eye = sps.eye
  999. else:
  1000. sparse = False
  1001. hstack = np.hstack
  1002. vstack = np.vstack
  1003. zeros = np.zeros
  1004. eye = np.eye
  1005. # Variables lbs and ubs (see below) may be changed, which feeds back into
  1006. # bounds, so copy.
  1007. bounds = np.array(bounds, copy=True)
  1008. # modify problem such that all variables have only non-negativity bounds
  1009. lbs = bounds[:, 0]
  1010. ubs = bounds[:, 1]
  1011. m_ub, n_ub = A_ub.shape
  1012. lb_none = np.equal(lbs, -np.inf)
  1013. ub_none = np.equal(ubs, np.inf)
  1014. lb_some = np.logical_not(lb_none)
  1015. ub_some = np.logical_not(ub_none)
  1016. # unbounded below: substitute xi = -xi' (unbounded above)
  1017. # if -inf <= xi <= ub, then -ub <= -xi <= inf, so swap and invert bounds
  1018. l_nolb_someub = np.logical_and(lb_none, ub_some)
  1019. i_nolb = np.nonzero(l_nolb_someub)[0]
  1020. lbs[l_nolb_someub], ubs[l_nolb_someub] = (
  1021. -ubs[l_nolb_someub], -lbs[l_nolb_someub])
  1022. lb_none = np.equal(lbs, -np.inf)
  1023. ub_none = np.equal(ubs, np.inf)
  1024. lb_some = np.logical_not(lb_none)
  1025. ub_some = np.logical_not(ub_none)
  1026. c[i_nolb] *= -1
  1027. if x0 is not None:
  1028. x0[i_nolb] *= -1
  1029. if len(i_nolb) > 0:
  1030. if A_ub.shape[0] > 0: # sometimes needed for sparse arrays... weird
  1031. A_ub[:, i_nolb] *= -1
  1032. if A_eq.shape[0] > 0:
  1033. A_eq[:, i_nolb] *= -1
  1034. # upper bound: add inequality constraint
  1035. i_newub, = ub_some.nonzero()
  1036. ub_newub = ubs[ub_some]
  1037. n_bounds = len(i_newub)
  1038. if n_bounds > 0:
  1039. shape = (n_bounds, A_ub.shape[1])
  1040. if sparse:
  1041. idxs = (np.arange(n_bounds), i_newub)
  1042. A_ub = vstack((A_ub, sps.csr_matrix((np.ones(n_bounds), idxs),
  1043. shape=shape)))
  1044. else:
  1045. A_ub = vstack((A_ub, np.zeros(shape)))
  1046. A_ub[np.arange(m_ub, A_ub.shape[0]), i_newub] = 1
  1047. b_ub = np.concatenate((b_ub, np.zeros(n_bounds)))
  1048. b_ub[m_ub:] = ub_newub
  1049. A1 = vstack((A_ub, A_eq))
  1050. b = np.concatenate((b_ub, b_eq))
  1051. c = np.concatenate((c, np.zeros((A_ub.shape[0],))))
  1052. if x0 is not None:
  1053. x0 = np.concatenate((x0, np.zeros((A_ub.shape[0],))))
  1054. # unbounded: substitute xi = xi+ + xi-
  1055. l_free = np.logical_and(lb_none, ub_none)
  1056. i_free = np.nonzero(l_free)[0]
  1057. n_free = len(i_free)
  1058. c = np.concatenate((c, np.zeros(n_free)))
  1059. if x0 is not None:
  1060. x0 = np.concatenate((x0, np.zeros(n_free)))
  1061. A1 = hstack((A1[:, :n_ub], -A1[:, i_free]))
  1062. c[n_ub:n_ub+n_free] = -c[i_free]
  1063. if x0 is not None:
  1064. i_free_neg = x0[i_free] < 0
  1065. x0[np.arange(n_ub, A1.shape[1])[i_free_neg]] = -x0[i_free[i_free_neg]]
  1066. x0[i_free[i_free_neg]] = 0
  1067. # add slack variables
  1068. A2 = vstack([eye(A_ub.shape[0]), zeros((A_eq.shape[0], A_ub.shape[0]))])
  1069. A = hstack([A1, A2])
  1070. # lower bound: substitute xi = xi' + lb
  1071. # now there is a constant term in objective
  1072. i_shift = np.nonzero(lb_some)[0]
  1073. lb_shift = lbs[lb_some].astype(float)
  1074. c0 += np.sum(lb_shift * c[i_shift])
  1075. if sparse:
  1076. b = b.reshape(-1, 1)
  1077. A = A.tocsc()
  1078. b -= (A[:, i_shift] * sps.diags(lb_shift)).sum(axis=1)
  1079. b = b.ravel()
  1080. else:
  1081. b -= (A[:, i_shift] * lb_shift).sum(axis=1)
  1082. if x0 is not None:
  1083. x0[i_shift] -= lb_shift
  1084. return A, b, c, c0, x0
  1085. def _round_to_power_of_two(x):
  1086. """
  1087. Round elements of the array to the nearest power of two.
  1088. """
  1089. return 2**np.around(np.log2(x))
  1090. def _autoscale(A, b, c, x0):
  1091. """
  1092. Scales the problem according to equilibration from [12].
  1093. Also normalizes the right hand side vector by its maximum element.
  1094. """
  1095. m, n = A.shape
  1096. C = 1
  1097. R = 1
  1098. if A.size > 0:
  1099. R = np.max(np.abs(A), axis=1)
  1100. if sps.issparse(A):
  1101. R = R.toarray().flatten()
  1102. R[R == 0] = 1
  1103. R = 1/_round_to_power_of_two(R)
  1104. A = sps.diags(R)*A if sps.issparse(A) else A*R.reshape(m, 1)
  1105. b = b*R
  1106. C = np.max(np.abs(A), axis=0)
  1107. if sps.issparse(A):
  1108. C = C.toarray().flatten()
  1109. C[C == 0] = 1
  1110. C = 1/_round_to_power_of_two(C)
  1111. A = A*sps.diags(C) if sps.issparse(A) else A*C
  1112. c = c*C
  1113. b_scale = np.max(np.abs(b)) if b.size > 0 else 1
  1114. if b_scale == 0:
  1115. b_scale = 1.
  1116. b = b/b_scale
  1117. if x0 is not None:
  1118. x0 = x0/b_scale*(1/C)
  1119. return A, b, c, x0, C, b_scale
  1120. def _unscale(x, C, b_scale):
  1121. """
  1122. Converts solution to _autoscale problem -> solution to original problem.
  1123. """
  1124. try:
  1125. n = len(C)
  1126. # fails if sparse or scalar; that's OK.
  1127. # this is only needed for original simplex (never sparse)
  1128. except TypeError:
  1129. n = len(x)
  1130. return x[:n]*b_scale*C
  1131. def _display_summary(message, status, fun, iteration):
  1132. """
  1133. Print the termination summary of the linear program
  1134. Parameters
  1135. ----------
  1136. message : str
  1137. A string descriptor of the exit status of the optimization.
  1138. status : int
  1139. An integer representing the exit status of the optimization::
  1140. 0 : Optimization terminated successfully
  1141. 1 : Iteration limit reached
  1142. 2 : Problem appears to be infeasible
  1143. 3 : Problem appears to be unbounded
  1144. 4 : Serious numerical difficulties encountered
  1145. fun : float
  1146. Value of the objective function.
  1147. iteration : iteration
  1148. The number of iterations performed.
  1149. """
  1150. print(message)
  1151. if status in (0, 1):
  1152. print(" Current function value: {0: <12.6f}".format(fun))
  1153. print(" Iterations: {0:d}".format(iteration))
  1154. def _postsolve(x, postsolve_args, complete=False):
  1155. """
  1156. Given solution x to presolved, standard form linear program x, add
  1157. fixed variables back into the problem and undo the variable substitutions
  1158. to get solution to original linear program. Also, calculate the objective
  1159. function value, slack in original upper bound constraints, and residuals
  1160. in original equality constraints.
  1161. Parameters
  1162. ----------
  1163. x : 1-D array
  1164. Solution vector to the standard-form problem.
  1165. postsolve_args : tuple
  1166. Data needed by _postsolve to convert the solution to the standard-form
  1167. problem into the solution to the original problem, including:
  1168. lp : A `scipy.optimize._linprog_util._LPProblem` consisting of the following fields:
  1169. c : 1D array
  1170. The coefficients of the linear objective function to be minimized.
  1171. A_ub : 2D array, optional
  1172. The inequality constraint matrix. Each row of ``A_ub`` specifies the
  1173. coefficients of a linear inequality constraint on ``x``.
  1174. b_ub : 1D array, optional
  1175. The inequality constraint vector. Each element represents an
  1176. upper bound on the corresponding value of ``A_ub @ x``.
  1177. A_eq : 2D array, optional
  1178. The equality constraint matrix. Each row of ``A_eq`` specifies the
  1179. coefficients of a linear equality constraint on ``x``.
  1180. b_eq : 1D array, optional
  1181. The equality constraint vector. Each element of ``A_eq @ x`` must equal
  1182. the corresponding element of ``b_eq``.
  1183. bounds : 2D array
  1184. The bounds of ``x``, lower bounds in the 1st column, upper
  1185. bounds in the 2nd column. The bounds are possibly tightened
  1186. by the presolve procedure.
  1187. x0 : 1D array, optional
  1188. Guess values of the decision variables, which will be refined by
  1189. the optimization algorithm. This argument is currently used only by the
  1190. 'revised simplex' method, and can only be used if `x0` represents a
  1191. basic feasible solution.
  1192. revstack: list of functions
  1193. the functions in the list reverse the operations of _presolve()
  1194. the function signature is x_org = f(x_mod), where x_mod is the result
  1195. of a presolve step and x_org the value at the start of the step
  1196. complete : bool
  1197. Whether the solution is was determined in presolve (``True`` if so)
  1198. Returns
  1199. -------
  1200. x : 1-D array
  1201. Solution vector to original linear programming problem
  1202. fun: float
  1203. optimal objective value for original problem
  1204. slack : 1-D array
  1205. The (non-negative) slack in the upper bound constraints, that is,
  1206. ``b_ub - A_ub @ x``
  1207. con : 1-D array
  1208. The (nominally zero) residuals of the equality constraints, that is,
  1209. ``b - A_eq @ x``
  1210. """
  1211. # note that all the inputs are the ORIGINAL, unmodified versions
  1212. # no rows, columns have been removed
  1213. c, A_ub, b_ub, A_eq, b_eq, bounds, x0, integrality = postsolve_args[0]
  1214. revstack, C, b_scale = postsolve_args[1:]
  1215. x = _unscale(x, C, b_scale)
  1216. # Undo variable substitutions of _get_Abc()
  1217. # if "complete", problem was solved in presolve; don't do anything here
  1218. n_x = bounds.shape[0]
  1219. if not complete and bounds is not None: # bounds are never none, probably
  1220. n_unbounded = 0
  1221. for i, bi in enumerate(bounds):
  1222. lbi = bi[0]
  1223. ubi = bi[1]
  1224. if lbi == -np.inf and ubi == np.inf:
  1225. n_unbounded += 1
  1226. x[i] = x[i] - x[n_x + n_unbounded - 1]
  1227. else:
  1228. if lbi == -np.inf:
  1229. x[i] = ubi - x[i]
  1230. else:
  1231. x[i] += lbi
  1232. # all the rest of the variables were artificial
  1233. x = x[:n_x]
  1234. # If there were variables removed from the problem, add them back into the
  1235. # solution vector
  1236. # Apply the functions in revstack (reverse direction)
  1237. for rev in reversed(revstack):
  1238. x = rev(x)
  1239. fun = x.dot(c)
  1240. slack = b_ub - A_ub.dot(x) # report slack for ORIGINAL UB constraints
  1241. # report residuals of ORIGINAL EQ constraints
  1242. con = b_eq - A_eq.dot(x)
  1243. return x, fun, slack, con
  1244. def _check_result(x, fun, status, slack, con, bounds, tol, message):
  1245. """
  1246. Check the validity of the provided solution.
  1247. A valid (optimal) solution satisfies all bounds, all slack variables are
  1248. negative and all equality constraint residuals are strictly non-zero.
  1249. Further, the lower-bounds, upper-bounds, slack and residuals contain
  1250. no nan values.
  1251. Parameters
  1252. ----------
  1253. x : 1-D array
  1254. Solution vector to original linear programming problem
  1255. fun: float
  1256. optimal objective value for original problem
  1257. status : int
  1258. An integer representing the exit status of the optimization::
  1259. 0 : Optimization terminated successfully
  1260. 1 : Iteration limit reached
  1261. 2 : Problem appears to be infeasible
  1262. 3 : Problem appears to be unbounded
  1263. 4 : Serious numerical difficulties encountered
  1264. slack : 1-D array
  1265. The (non-negative) slack in the upper bound constraints, that is,
  1266. ``b_ub - A_ub @ x``
  1267. con : 1-D array
  1268. The (nominally zero) residuals of the equality constraints, that is,
  1269. ``b - A_eq @ x``
  1270. bounds : 2D array
  1271. The bounds on the original variables ``x``
  1272. message : str
  1273. A string descriptor of the exit status of the optimization.
  1274. tol : float
  1275. Termination tolerance; see [1]_ Section 4.5.
  1276. Returns
  1277. -------
  1278. status : int
  1279. An integer representing the exit status of the optimization::
  1280. 0 : Optimization terminated successfully
  1281. 1 : Iteration limit reached
  1282. 2 : Problem appears to be infeasible
  1283. 3 : Problem appears to be unbounded
  1284. 4 : Serious numerical difficulties encountered
  1285. message : str
  1286. A string descriptor of the exit status of the optimization.
  1287. """
  1288. # Somewhat arbitrary
  1289. tol = np.sqrt(tol) * 10
  1290. if x is None:
  1291. # HiGHS does not provide x if infeasible/unbounded
  1292. if status == 0: # Observed with HiGHS Simplex Primal
  1293. status = 4
  1294. message = ("The solver did not provide a solution nor did it "
  1295. "report a failure. Please submit a bug report.")
  1296. return status, message
  1297. contains_nans = (
  1298. np.isnan(x).any()
  1299. or np.isnan(fun)
  1300. or np.isnan(slack).any()
  1301. or np.isnan(con).any()
  1302. )
  1303. if contains_nans:
  1304. is_feasible = False
  1305. else:
  1306. invalid_bounds = (x < bounds[:, 0] - tol).any() or (x > bounds[:, 1] + tol).any()
  1307. invalid_slack = status != 3 and (slack < -tol).any()
  1308. invalid_con = status != 3 and (np.abs(con) > tol).any()
  1309. is_feasible = not (invalid_bounds or invalid_slack or invalid_con)
  1310. if status == 0 and not is_feasible:
  1311. status = 4
  1312. message = ("The solution does not satisfy the constraints within the "
  1313. "required tolerance of " + "{:.2E}".format(tol) + ", yet "
  1314. "no errors were raised and there is no certificate of "
  1315. "infeasibility or unboundedness. Check whether "
  1316. "the slack and constraint residuals are acceptable; "
  1317. "if not, consider enabling presolve, adjusting the "
  1318. "tolerance option(s), and/or using a different method. "
  1319. "Please consider submitting a bug report.")
  1320. elif status == 2 and is_feasible:
  1321. # Occurs if the simplex method exits after phase one with a very
  1322. # nearly basic feasible solution. Postsolving can make the solution
  1323. # basic, however, this solution is NOT optimal
  1324. status = 4
  1325. message = ("The solution is feasible, but the solver did not report "
  1326. "that the solution was optimal. Please try a different "
  1327. "method.")
  1328. return status, message