Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/scipy/optimize/_linprog_util.py: 5%

487 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-12 06:31 +0000

1""" 

2Method agnostic utility functions for linear progamming 

3""" 

4 

5import numpy as np 

6import scipy.sparse as sps 

7from warnings import warn 

8from ._optimize import OptimizeWarning 

9from scipy.optimize._remove_redundancy import ( 

10 _remove_redundancy_svd, _remove_redundancy_pivot_sparse, 

11 _remove_redundancy_pivot_dense, _remove_redundancy_id 

12 ) 

13from collections import namedtuple 

14 

15_LPProblem = namedtuple('_LPProblem', 

16 'c A_ub b_ub A_eq b_eq bounds x0 integrality') 

17_LPProblem.__new__.__defaults__ = (None,) * 7 # make c the only required arg 

18_LPProblem.__doc__ = \ 

19 """ Represents a linear-programming problem. 

20 

21 Attributes 

22 ---------- 

23 c : 1D array 

24 The coefficients of the linear objective function to be minimized. 

25 A_ub : 2D array, optional 

26 The inequality constraint matrix. Each row of ``A_ub`` specifies the 

27 coefficients of a linear inequality constraint on ``x``. 

28 b_ub : 1D array, optional 

29 The inequality constraint vector. Each element represents an 

30 upper bound on the corresponding value of ``A_ub @ x``. 

31 A_eq : 2D array, optional 

32 The equality constraint matrix. Each row of ``A_eq`` specifies the 

33 coefficients of a linear equality constraint on ``x``. 

34 b_eq : 1D array, optional 

35 The equality constraint vector. Each element of ``A_eq @ x`` must equal 

36 the corresponding element of ``b_eq``. 

37 bounds : various valid formats, optional 

38 The bounds of ``x``, as ``min`` and ``max`` pairs. 

39 If bounds are specified for all N variables separately, valid formats 

40 are: 

41 * a 2D array (N x 2); 

42 * a sequence of N sequences, each with 2 values. 

43 If all variables have the same bounds, the bounds can be specified as 

44 a 1-D or 2-D array or sequence with 2 scalar values. 

45 If all variables have a lower bound of 0 and no upper bound, the bounds 

46 parameter can be omitted (or given as None). 

47 Absent lower and/or upper bounds can be specified as -numpy.inf (no 

48 lower bound), numpy.inf (no upper bound) or None (both). 

49 x0 : 1D array, optional 

50 Guess values of the decision variables, which will be refined by 

51 the optimization algorithm. This argument is currently used only by the 

52 'revised simplex' method, and can only be used if `x0` represents a 

53 basic feasible solution. 

54 integrality : 1-D array or int, optional 

55 Indicates the type of integrality constraint on each decision variable. 

56 

57 ``0`` : Continuous variable; no integrality constraint. 

58 

59 ``1`` : Integer variable; decision variable must be an integer 

60 within `bounds`. 

61 

62 ``2`` : Semi-continuous variable; decision variable must be within 

63 `bounds` or take value ``0``. 

64 

65 ``3`` : Semi-integer variable; decision variable must be an integer 

66 within `bounds` or take value ``0``. 

67 

68 By default, all variables are continuous. 

69 

70 For mixed integrality constraints, supply an array of shape `c.shape`. 

71 To infer a constraint on each decision variable from shorter inputs, 

72 the argument will be broadcasted to `c.shape` using `np.broadcast_to`. 

73 

74 This argument is currently used only by the ``'highs'`` method and 

75 ignored otherwise. 

76 

77 Notes 

78 ----- 

79 This namedtuple supports 2 ways of initialization: 

80 >>> lp1 = _LPProblem(c=[-1, 4], A_ub=[[-3, 1], [1, 2]], b_ub=[6, 4]) 

81 >>> lp2 = _LPProblem([-1, 4], [[-3, 1], [1, 2]], [6, 4]) 

82 

83 Note that only ``c`` is a required argument here, whereas all other arguments 

84 ``A_ub``, ``b_ub``, ``A_eq``, ``b_eq``, ``bounds``, ``x0`` are optional with 

85 default values of None. 

86 For example, ``A_eq`` and ``b_eq`` can be set without ``A_ub`` or ``b_ub``: 

87 >>> lp3 = _LPProblem(c=[-1, 4], A_eq=[[2, 1]], b_eq=[10]) 

88 """ 

89 

90 

91def _check_sparse_inputs(options, meth, A_ub, A_eq): 

92 """ 

93 Check the provided ``A_ub`` and ``A_eq`` matrices conform to the specified 

94 optional sparsity variables. 

95 

96 Parameters 

97 ---------- 

98 A_ub : 2-D array, optional 

99 2-D array such that ``A_ub @ x`` gives the values of the upper-bound 

100 inequality constraints at ``x``. 

101 A_eq : 2-D array, optional 

102 2-D array such that ``A_eq @ x`` gives the values of the equality 

103 constraints at ``x``. 

104 options : dict 

105 A dictionary of solver options. All methods accept the following 

106 generic options: 

107 

108 maxiter : int 

109 Maximum number of iterations to perform. 

110 disp : bool 

111 Set to True to print convergence messages. 

112 

113 For method-specific options, see :func:`show_options('linprog')`. 

114 method : str, optional 

115 The algorithm used to solve the standard form problem. 

116 

117 Returns 

118 ------- 

119 A_ub : 2-D array, optional 

120 2-D array such that ``A_ub @ x`` gives the values of the upper-bound 

121 inequality constraints at ``x``. 

122 A_eq : 2-D array, optional 

123 2-D array such that ``A_eq @ x`` gives the values of the equality 

124 constraints at ``x``. 

125 options : dict 

126 A dictionary of solver options. All methods accept the following 

127 generic options: 

128 

129 maxiter : int 

130 Maximum number of iterations to perform. 

131 disp : bool 

132 Set to True to print convergence messages. 

133 

134 For method-specific options, see :func:`show_options('linprog')`. 

135 """ 

136 # This is an undocumented option for unit testing sparse presolve 

137 _sparse_presolve = options.pop('_sparse_presolve', False) 

138 if _sparse_presolve and A_eq is not None: 

139 A_eq = sps.coo_matrix(A_eq) 

140 if _sparse_presolve and A_ub is not None: 

141 A_ub = sps.coo_matrix(A_ub) 

142 

143 sparse_constraint = sps.issparse(A_eq) or sps.issparse(A_ub) 

144 

145 preferred_methods = {"highs", "highs-ds", "highs-ipm"} 

146 dense_methods = {"simplex", "revised simplex"} 

147 if meth in dense_methods and sparse_constraint: 

148 raise ValueError(f"Method '{meth}' does not support sparse " 

149 "constraint matrices. Please consider using one of " 

150 f"{preferred_methods}.") 

151 

152 sparse = options.get('sparse', False) 

153 if not sparse and sparse_constraint and meth == 'interior-point': 

154 options['sparse'] = True 

155 warn("Sparse constraint matrix detected; setting 'sparse':True.", 

156 OptimizeWarning, stacklevel=4) 

157 return options, A_ub, A_eq 

158 

159 

160def _format_A_constraints(A, n_x, sparse_lhs=False): 

161 """Format the left hand side of the constraints to a 2-D array 

162 

163 Parameters 

164 ---------- 

165 A : 2-D array 

166 2-D array such that ``A @ x`` gives the values of the upper-bound 

167 (in)equality constraints at ``x``. 

168 n_x : int 

169 The number of variables in the linear programming problem. 

170 sparse_lhs : bool 

171 Whether either of `A_ub` or `A_eq` are sparse. If true return a 

172 coo_matrix instead of a numpy array. 

173 

174 Returns 

175 ------- 

176 np.ndarray or sparse.coo_matrix 

177 2-D array such that ``A @ x`` gives the values of the upper-bound 

178 (in)equality constraints at ``x``. 

179 

180 """ 

181 if sparse_lhs: 

182 return sps.coo_matrix( 

183 (0, n_x) if A is None else A, dtype=float, copy=True 

184 ) 

185 elif A is None: 

186 return np.zeros((0, n_x), dtype=float) 

187 else: 

188 return np.array(A, dtype=float, copy=True) 

189 

190 

191def _format_b_constraints(b): 

192 """Format the upper bounds of the constraints to a 1-D array 

193 

194 Parameters 

195 ---------- 

196 b : 1-D array 

197 1-D array of values representing the upper-bound of each (in)equality 

198 constraint (row) in ``A``. 

199 

200 Returns 

201 ------- 

202 1-D np.array 

203 1-D array of values representing the upper-bound of each (in)equality 

204 constraint (row) in ``A``. 

205 

206 """ 

207 if b is None: 

208 return np.array([], dtype=float) 

209 b = np.array(b, dtype=float, copy=True).squeeze() 

210 return b if b.size != 1 else b.reshape((-1)) 

211 

212 

213def _clean_inputs(lp): 

214 """ 

215 Given user inputs for a linear programming problem, return the 

216 objective vector, upper bound constraints, equality constraints, 

217 and simple bounds in a preferred format. 

218 

219 Parameters 

220 ---------- 

221 lp : A `scipy.optimize._linprog_util._LPProblem` consisting of the following fields: 

222 

223 c : 1D array 

224 The coefficients of the linear objective function to be minimized. 

225 A_ub : 2D array, optional 

226 The inequality constraint matrix. Each row of ``A_ub`` specifies the 

227 coefficients of a linear inequality constraint on ``x``. 

228 b_ub : 1D array, optional 

229 The inequality constraint vector. Each element represents an 

230 upper bound on the corresponding value of ``A_ub @ x``. 

231 A_eq : 2D array, optional 

232 The equality constraint matrix. Each row of ``A_eq`` specifies the 

233 coefficients of a linear equality constraint on ``x``. 

234 b_eq : 1D array, optional 

235 The equality constraint vector. Each element of ``A_eq @ x`` must equal 

236 the corresponding element of ``b_eq``. 

237 bounds : various valid formats, optional 

238 The bounds of ``x``, as ``min`` and ``max`` pairs. 

239 If bounds are specified for all N variables separately, valid formats are: 

240 * a 2D array (2 x N or N x 2); 

241 * a sequence of N sequences, each with 2 values. 

242 If all variables have the same bounds, a single pair of values can 

243 be specified. Valid formats are: 

244 * a sequence with 2 scalar values; 

245 * a sequence with a single element containing 2 scalar values. 

246 If all variables have a lower bound of 0 and no upper bound, the bounds 

247 parameter can be omitted (or given as None). 

248 x0 : 1D array, optional 

249 Guess values of the decision variables, which will be refined by 

250 the optimization algorithm. This argument is currently used only by the 

251 'revised simplex' method, and can only be used if `x0` represents a 

252 basic feasible solution. 

253 

254 Returns 

255 ------- 

256 lp : A `scipy.optimize._linprog_util._LPProblem` consisting of the following fields: 

257 

258 c : 1D array 

259 The coefficients of the linear objective function to be minimized. 

260 A_ub : 2D array, optional 

261 The inequality constraint matrix. Each row of ``A_ub`` specifies the 

262 coefficients of a linear inequality constraint on ``x``. 

263 b_ub : 1D array, optional 

264 The inequality constraint vector. Each element represents an 

265 upper bound on the corresponding value of ``A_ub @ x``. 

266 A_eq : 2D array, optional 

267 The equality constraint matrix. Each row of ``A_eq`` specifies the 

268 coefficients of a linear equality constraint on ``x``. 

269 b_eq : 1D array, optional 

270 The equality constraint vector. Each element of ``A_eq @ x`` must equal 

271 the corresponding element of ``b_eq``. 

272 bounds : 2D array 

273 The bounds of ``x``, as ``min`` and ``max`` pairs, one for each of the N 

274 elements of ``x``. The N x 2 array contains lower bounds in the first 

275 column and upper bounds in the 2nd. Unbounded variables have lower 

276 bound -np.inf and/or upper bound np.inf. 

277 x0 : 1D array, optional 

278 Guess values of the decision variables, which will be refined by 

279 the optimization algorithm. This argument is currently used only by the 

280 'revised simplex' method, and can only be used if `x0` represents a 

281 basic feasible solution. 

282 

283 """ 

284 c, A_ub, b_ub, A_eq, b_eq, bounds, x0, integrality = lp 

285 

286 if c is None: 

287 raise TypeError 

288 

289 try: 

290 c = np.array(c, dtype=np.float64, copy=True).squeeze() 

291 except ValueError as e: 

292 raise TypeError( 

293 "Invalid input for linprog: c must be a 1-D array of numerical " 

294 "coefficients") from e 

295 else: 

296 # If c is a single value, convert it to a 1-D array. 

297 if c.size == 1: 

298 c = c.reshape((-1)) 

299 

300 n_x = len(c) 

301 if n_x == 0 or len(c.shape) != 1: 

302 raise ValueError( 

303 "Invalid input for linprog: c must be a 1-D array and must " 

304 "not have more than one non-singleton dimension") 

305 if not np.isfinite(c).all(): 

306 raise ValueError( 

307 "Invalid input for linprog: c must not contain values " 

308 "inf, nan, or None") 

309 

310 sparse_lhs = sps.issparse(A_eq) or sps.issparse(A_ub) 

311 try: 

312 A_ub = _format_A_constraints(A_ub, n_x, sparse_lhs=sparse_lhs) 

313 except ValueError as e: 

314 raise TypeError( 

315 "Invalid input for linprog: A_ub must be a 2-D array " 

316 "of numerical values") from e 

317 else: 

318 n_ub = A_ub.shape[0] 

319 if len(A_ub.shape) != 2 or A_ub.shape[1] != n_x: 

320 raise ValueError( 

321 "Invalid input for linprog: A_ub must have exactly two " 

322 "dimensions, and the number of columns in A_ub must be " 

323 "equal to the size of c") 

324 if (sps.issparse(A_ub) and not np.isfinite(A_ub.data).all() 

325 or not sps.issparse(A_ub) and not np.isfinite(A_ub).all()): 

326 raise ValueError( 

327 "Invalid input for linprog: A_ub must not contain values " 

328 "inf, nan, or None") 

329 

330 try: 

331 b_ub = _format_b_constraints(b_ub) 

332 except ValueError as e: 

333 raise TypeError( 

334 "Invalid input for linprog: b_ub must be a 1-D array of " 

335 "numerical values, each representing the upper bound of an " 

336 "inequality constraint (row) in A_ub") from e 

337 else: 

338 if b_ub.shape != (n_ub,): 

339 raise ValueError( 

340 "Invalid input for linprog: b_ub must be a 1-D array; b_ub " 

341 "must not have more than one non-singleton dimension and " 

342 "the number of rows in A_ub must equal the number of values " 

343 "in b_ub") 

344 if not np.isfinite(b_ub).all(): 

345 raise ValueError( 

346 "Invalid input for linprog: b_ub must not contain values " 

347 "inf, nan, or None") 

348 

349 try: 

350 A_eq = _format_A_constraints(A_eq, n_x, sparse_lhs=sparse_lhs) 

351 except ValueError as e: 

352 raise TypeError( 

353 "Invalid input for linprog: A_eq must be a 2-D array " 

354 "of numerical values") from e 

355 else: 

356 n_eq = A_eq.shape[0] 

357 if len(A_eq.shape) != 2 or A_eq.shape[1] != n_x: 

358 raise ValueError( 

359 "Invalid input for linprog: A_eq must have exactly two " 

360 "dimensions, and the number of columns in A_eq must be " 

361 "equal to the size of c") 

362 

363 if (sps.issparse(A_eq) and not np.isfinite(A_eq.data).all() 

364 or not sps.issparse(A_eq) and not np.isfinite(A_eq).all()): 

365 raise ValueError( 

366 "Invalid input for linprog: A_eq must not contain values " 

367 "inf, nan, or None") 

368 

369 try: 

370 b_eq = _format_b_constraints(b_eq) 

371 except ValueError as e: 

372 raise TypeError( 

373 "Invalid input for linprog: b_eq must be a dense, 1-D array of " 

374 "numerical values, each representing the right hand side of an " 

375 "equality constraint (row) in A_eq") from e 

376 else: 

377 if b_eq.shape != (n_eq,): 

378 raise ValueError( 

379 "Invalid input for linprog: b_eq must be a 1-D array; b_eq " 

380 "must not have more than one non-singleton dimension and " 

381 "the number of rows in A_eq must equal the number of values " 

382 "in b_eq") 

383 if not np.isfinite(b_eq).all(): 

384 raise ValueError( 

385 "Invalid input for linprog: b_eq must not contain values " 

386 "inf, nan, or None") 

387 

388 # x0 gives a (optional) starting solution to the solver. If x0 is None, 

389 # skip the checks. Initial solution will be generated automatically. 

390 if x0 is not None: 

391 try: 

392 x0 = np.array(x0, dtype=float, copy=True).squeeze() 

393 except ValueError as e: 

394 raise TypeError( 

395 "Invalid input for linprog: x0 must be a 1-D array of " 

396 "numerical coefficients") from e 

397 if x0.ndim == 0: 

398 x0 = x0.reshape((-1)) 

399 if len(x0) == 0 or x0.ndim != 1: 

400 raise ValueError( 

401 "Invalid input for linprog: x0 should be a 1-D array; it " 

402 "must not have more than one non-singleton dimension") 

403 if not x0.size == c.size: 

404 raise ValueError( 

405 "Invalid input for linprog: x0 and c should contain the " 

406 "same number of elements") 

407 if not np.isfinite(x0).all(): 

408 raise ValueError( 

409 "Invalid input for linprog: x0 must not contain values " 

410 "inf, nan, or None") 

411 

412 # Bounds can be one of these formats: 

413 # (1) a 2-D array or sequence, with shape N x 2 

414 # (2) a 1-D or 2-D sequence or array with 2 scalars 

415 # (3) None (or an empty sequence or array) 

416 # Unspecified bounds can be represented by None or (-)np.inf. 

417 # All formats are converted into a N x 2 np.array with (-)np.inf where 

418 # bounds are unspecified. 

419 

420 # Prepare clean bounds array 

421 bounds_clean = np.zeros((n_x, 2), dtype=float) 

422 

423 # Convert to a numpy array. 

424 # np.array(..,dtype=float) raises an error if dimensions are inconsistent 

425 # or if there are invalid data types in bounds. Just add a linprog prefix 

426 # to the error and re-raise. 

427 # Creating at least a 2-D array simplifies the cases to distinguish below. 

428 if bounds is None or np.array_equal(bounds, []) or np.array_equal(bounds, [[]]): 

429 bounds = (0, np.inf) 

430 try: 

431 bounds_conv = np.atleast_2d(np.array(bounds, dtype=float)) 

432 except ValueError as e: 

433 raise ValueError( 

434 "Invalid input for linprog: unable to interpret bounds, " 

435 "check values and dimensions: " + e.args[0]) from e 

436 except TypeError as e: 

437 raise TypeError( 

438 "Invalid input for linprog: unable to interpret bounds, " 

439 "check values and dimensions: " + e.args[0]) from e 

440 

441 # Check bounds options 

442 bsh = bounds_conv.shape 

443 if len(bsh) > 2: 

444 # Do not try to handle multidimensional bounds input 

445 raise ValueError( 

446 "Invalid input for linprog: provide a 2-D array for bounds, " 

447 "not a {:d}-D array.".format(len(bsh))) 

448 elif np.all(bsh == (n_x, 2)): 

449 # Regular N x 2 array 

450 bounds_clean = bounds_conv 

451 elif (np.all(bsh == (2, 1)) or np.all(bsh == (1, 2))): 

452 # 2 values: interpret as overall lower and upper bound 

453 bounds_flat = bounds_conv.flatten() 

454 bounds_clean[:, 0] = bounds_flat[0] 

455 bounds_clean[:, 1] = bounds_flat[1] 

456 elif np.all(bsh == (2, n_x)): 

457 # Reject a 2 x N array 

458 raise ValueError( 

459 "Invalid input for linprog: provide a {:d} x 2 array for bounds, " 

460 "not a 2 x {:d} array.".format(n_x, n_x)) 

461 else: 

462 raise ValueError( 

463 "Invalid input for linprog: unable to interpret bounds with this " 

464 "dimension tuple: {0}.".format(bsh)) 

465 

466 # The process above creates nan-s where the input specified None 

467 # Convert the nan-s in the 1st column to -np.inf and in the 2nd column 

468 # to np.inf 

469 i_none = np.isnan(bounds_clean[:, 0]) 

470 bounds_clean[i_none, 0] = -np.inf 

471 i_none = np.isnan(bounds_clean[:, 1]) 

472 bounds_clean[i_none, 1] = np.inf 

473 

474 return _LPProblem(c, A_ub, b_ub, A_eq, b_eq, bounds_clean, x0, integrality) 

475 

476 

477def _presolve(lp, rr, rr_method, tol=1e-9): 

478 """ 

479 Given inputs for a linear programming problem in preferred format, 

480 presolve the problem: identify trivial infeasibilities, redundancies, 

481 and unboundedness, tighten bounds where possible, and eliminate fixed 

482 variables. 

483 

484 Parameters 

485 ---------- 

486 lp : A `scipy.optimize._linprog_util._LPProblem` consisting of the following fields: 

487 

488 c : 1D array 

489 The coefficients of the linear objective function to be minimized. 

490 A_ub : 2D array, optional 

491 The inequality constraint matrix. Each row of ``A_ub`` specifies the 

492 coefficients of a linear inequality constraint on ``x``. 

493 b_ub : 1D array, optional 

494 The inequality constraint vector. Each element represents an 

495 upper bound on the corresponding value of ``A_ub @ x``. 

496 A_eq : 2D array, optional 

497 The equality constraint matrix. Each row of ``A_eq`` specifies the 

498 coefficients of a linear equality constraint on ``x``. 

499 b_eq : 1D array, optional 

500 The equality constraint vector. Each element of ``A_eq @ x`` must equal 

501 the corresponding element of ``b_eq``. 

502 bounds : 2D array 

503 The bounds of ``x``, as ``min`` and ``max`` pairs, one for each of the N 

504 elements of ``x``. The N x 2 array contains lower bounds in the first 

505 column and upper bounds in the 2nd. Unbounded variables have lower 

506 bound -np.inf and/or upper bound np.inf. 

507 x0 : 1D array, optional 

508 Guess values of the decision variables, which will be refined by 

509 the optimization algorithm. This argument is currently used only by the 

510 'revised simplex' method, and can only be used if `x0` represents a 

511 basic feasible solution. 

512 

513 rr : bool 

514 If ``True`` attempts to eliminate any redundant rows in ``A_eq``. 

515 Set False if ``A_eq`` is known to be of full row rank, or if you are 

516 looking for a potential speedup (at the expense of reliability). 

517 rr_method : string 

518 Method used to identify and remove redundant rows from the 

519 equality constraint matrix after presolve. 

520 tol : float 

521 The tolerance which determines when a solution is "close enough" to 

522 zero in Phase 1 to be considered a basic feasible solution or close 

523 enough to positive to serve as an optimal solution. 

524 

525 Returns 

526 ------- 

527 lp : A `scipy.optimize._linprog_util._LPProblem` consisting of the following fields: 

528 

529 c : 1D array 

530 The coefficients of the linear objective function to be minimized. 

531 A_ub : 2D array, optional 

532 The inequality constraint matrix. Each row of ``A_ub`` specifies the 

533 coefficients of a linear inequality constraint on ``x``. 

534 b_ub : 1D array, optional 

535 The inequality constraint vector. Each element represents an 

536 upper bound on the corresponding value of ``A_ub @ x``. 

537 A_eq : 2D array, optional 

538 The equality constraint matrix. Each row of ``A_eq`` specifies the 

539 coefficients of a linear equality constraint on ``x``. 

540 b_eq : 1D array, optional 

541 The equality constraint vector. Each element of ``A_eq @ x`` must equal 

542 the corresponding element of ``b_eq``. 

543 bounds : 2D array 

544 The bounds of ``x``, as ``min`` and ``max`` pairs, possibly tightened. 

545 x0 : 1D array, optional 

546 Guess values of the decision variables, which will be refined by 

547 the optimization algorithm. This argument is currently used only by the 

548 'revised simplex' method, and can only be used if `x0` represents a 

549 basic feasible solution. 

550 

551 c0 : 1D array 

552 Constant term in objective function due to fixed (and eliminated) 

553 variables. 

554 x : 1D array 

555 Solution vector (when the solution is trivial and can be determined 

556 in presolve) 

557 revstack: list of functions 

558 the functions in the list reverse the operations of _presolve() 

559 the function signature is x_org = f(x_mod), where x_mod is the result 

560 of a presolve step and x_org the value at the start of the step 

561 (currently, the revstack contains only one function) 

562 complete: bool 

563 Whether the solution is complete (solved or determined to be infeasible 

564 or unbounded in presolve) 

565 status : int 

566 An integer representing the exit status of the optimization:: 

567 

568 0 : Optimization terminated successfully 

569 1 : Iteration limit reached 

570 2 : Problem appears to be infeasible 

571 3 : Problem appears to be unbounded 

572 4 : Serious numerical difficulties encountered 

573 

574 message : str 

575 A string descriptor of the exit status of the optimization. 

576 

577 References 

578 ---------- 

579 .. [5] Andersen, Erling D. "Finding all linearly dependent rows in 

580 large-scale linear programming." Optimization Methods and Software 

581 6.3 (1995): 219-227. 

582 .. [8] Andersen, Erling D., and Knud D. Andersen. "Presolving in linear 

583 programming." Mathematical Programming 71.2 (1995): 221-245. 

584 

585 """ 

586 # ideas from Reference [5] by Andersen and Andersen 

587 # however, unlike the reference, this is performed before converting 

588 # problem to standard form 

589 # There are a few advantages: 

590 # * artificial variables have not been added, so matrices are smaller 

591 # * bounds have not been converted to constraints yet. (It is better to 

592 # do that after presolve because presolve may adjust the simple bounds.) 

593 # There are many improvements that can be made, namely: 

594 # * implement remaining checks from [5] 

595 # * loop presolve until no additional changes are made 

596 # * implement additional efficiency improvements in redundancy removal [2] 

597 

598 c, A_ub, b_ub, A_eq, b_eq, bounds, x0, _ = lp 

599 

600 revstack = [] # record of variables eliminated from problem 

601 # constant term in cost function may be added if variables are eliminated 

602 c0 = 0 

603 complete = False # complete is True if detected infeasible/unbounded 

604 x = np.zeros(c.shape) # this is solution vector if completed in presolve 

605 

606 status = 0 # all OK unless determined otherwise 

607 message = "" 

608 

609 # Lower and upper bounds. Copy to prevent feedback. 

610 lb = bounds[:, 0].copy() 

611 ub = bounds[:, 1].copy() 

612 

613 m_eq, n = A_eq.shape 

614 m_ub, n = A_ub.shape 

615 

616 if (rr_method is not None 

617 and rr_method.lower() not in {"svd", "pivot", "id"}): 

618 message = ("'" + str(rr_method) + "' is not a valid option " 

619 "for redundancy removal. Valid options are 'SVD', " 

620 "'pivot', and 'ID'.") 

621 raise ValueError(message) 

622 

623 if sps.issparse(A_eq): 

624 A_eq = A_eq.tocsr() 

625 A_ub = A_ub.tocsr() 

626 

627 def where(A): 

628 return A.nonzero() 

629 

630 vstack = sps.vstack 

631 else: 

632 where = np.where 

633 vstack = np.vstack 

634 

635 # upper bounds > lower bounds 

636 if np.any(ub < lb) or np.any(lb == np.inf) or np.any(ub == -np.inf): 

637 status = 2 

638 message = ("The problem is (trivially) infeasible since one " 

639 "or more upper bounds are smaller than the corresponding " 

640 "lower bounds, a lower bound is np.inf or an upper bound " 

641 "is -np.inf.") 

642 complete = True 

643 return (_LPProblem(c, A_ub, b_ub, A_eq, b_eq, bounds, x0), 

644 c0, x, revstack, complete, status, message) 

645 

646 # zero row in equality constraints 

647 zero_row = np.array(np.sum(A_eq != 0, axis=1) == 0).flatten() 

648 if np.any(zero_row): 

649 if np.any( 

650 np.logical_and( 

651 zero_row, 

652 np.abs(b_eq) > tol)): # test_zero_row_1 

653 # infeasible if RHS is not zero 

654 status = 2 

655 message = ("The problem is (trivially) infeasible due to a row " 

656 "of zeros in the equality constraint matrix with a " 

657 "nonzero corresponding constraint value.") 

658 complete = True 

659 return (_LPProblem(c, A_ub, b_ub, A_eq, b_eq, bounds, x0), 

660 c0, x, revstack, complete, status, message) 

661 else: # test_zero_row_2 

662 # if RHS is zero, we can eliminate this equation entirely 

663 A_eq = A_eq[np.logical_not(zero_row), :] 

664 b_eq = b_eq[np.logical_not(zero_row)] 

665 

666 # zero row in inequality constraints 

667 zero_row = np.array(np.sum(A_ub != 0, axis=1) == 0).flatten() 

668 if np.any(zero_row): 

669 if np.any(np.logical_and(zero_row, b_ub < -tol)): # test_zero_row_1 

670 # infeasible if RHS is less than zero (because LHS is zero) 

671 status = 2 

672 message = ("The problem is (trivially) infeasible due to a row " 

673 "of zeros in the equality constraint matrix with a " 

674 "nonzero corresponding constraint value.") 

675 complete = True 

676 return (_LPProblem(c, A_ub, b_ub, A_eq, b_eq, bounds, x0), 

677 c0, x, revstack, complete, status, message) 

678 else: # test_zero_row_2 

679 # if LHS is >= 0, we can eliminate this constraint entirely 

680 A_ub = A_ub[np.logical_not(zero_row), :] 

681 b_ub = b_ub[np.logical_not(zero_row)] 

682 

683 # zero column in (both) constraints 

684 # this indicates that a variable isn't constrained and can be removed 

685 A = vstack((A_eq, A_ub)) 

686 if A.shape[0] > 0: 

687 zero_col = np.array(np.sum(A != 0, axis=0) == 0).flatten() 

688 # variable will be at upper or lower bound, depending on objective 

689 x[np.logical_and(zero_col, c < 0)] = ub[ 

690 np.logical_and(zero_col, c < 0)] 

691 x[np.logical_and(zero_col, c > 0)] = lb[ 

692 np.logical_and(zero_col, c > 0)] 

693 if np.any(np.isinf(x)): # if an unconstrained variable has no bound 

694 status = 3 

695 message = ("If feasible, the problem is (trivially) unbounded " 

696 "due to a zero column in the constraint matrices. If " 

697 "you wish to check whether the problem is infeasible, " 

698 "turn presolve off.") 

699 complete = True 

700 return (_LPProblem(c, A_ub, b_ub, A_eq, b_eq, bounds, x0), 

701 c0, x, revstack, complete, status, message) 

702 # variables will equal upper/lower bounds will be removed later 

703 lb[np.logical_and(zero_col, c < 0)] = ub[ 

704 np.logical_and(zero_col, c < 0)] 

705 ub[np.logical_and(zero_col, c > 0)] = lb[ 

706 np.logical_and(zero_col, c > 0)] 

707 

708 # row singleton in equality constraints 

709 # this fixes a variable and removes the constraint 

710 singleton_row = np.array(np.sum(A_eq != 0, axis=1) == 1).flatten() 

711 rows = where(singleton_row)[0] 

712 cols = where(A_eq[rows, :])[1] 

713 if len(rows) > 0: 

714 for row, col in zip(rows, cols): 

715 val = b_eq[row] / A_eq[row, col] 

716 if not lb[col] - tol <= val <= ub[col] + tol: 

717 # infeasible if fixed value is not within bounds 

718 status = 2 

719 message = ("The problem is (trivially) infeasible because a " 

720 "singleton row in the equality constraints is " 

721 "inconsistent with the bounds.") 

722 complete = True 

723 return (_LPProblem(c, A_ub, b_ub, A_eq, b_eq, bounds, x0), 

724 c0, x, revstack, complete, status, message) 

725 else: 

726 # sets upper and lower bounds at that fixed value - variable 

727 # will be removed later 

728 lb[col] = val 

729 ub[col] = val 

730 A_eq = A_eq[np.logical_not(singleton_row), :] 

731 b_eq = b_eq[np.logical_not(singleton_row)] 

732 

733 # row singleton in inequality constraints 

734 # this indicates a simple bound and the constraint can be removed 

735 # simple bounds may be adjusted here 

736 # After all of the simple bound information is combined here, get_Abc will 

737 # turn the simple bounds into constraints 

738 singleton_row = np.array(np.sum(A_ub != 0, axis=1) == 1).flatten() 

739 cols = where(A_ub[singleton_row, :])[1] 

740 rows = where(singleton_row)[0] 

741 if len(rows) > 0: 

742 for row, col in zip(rows, cols): 

743 val = b_ub[row] / A_ub[row, col] 

744 if A_ub[row, col] > 0: # upper bound 

745 if val < lb[col] - tol: # infeasible 

746 complete = True 

747 elif val < ub[col]: # new upper bound 

748 ub[col] = val 

749 else: # lower bound 

750 if val > ub[col] + tol: # infeasible 

751 complete = True 

752 elif val > lb[col]: # new lower bound 

753 lb[col] = val 

754 if complete: 

755 status = 2 

756 message = ("The problem is (trivially) infeasible because a " 

757 "singleton row in the upper bound constraints is " 

758 "inconsistent with the bounds.") 

759 return (_LPProblem(c, A_ub, b_ub, A_eq, b_eq, bounds, x0), 

760 c0, x, revstack, complete, status, message) 

761 A_ub = A_ub[np.logical_not(singleton_row), :] 

762 b_ub = b_ub[np.logical_not(singleton_row)] 

763 

764 # identical bounds indicate that variable can be removed 

765 i_f = np.abs(lb - ub) < tol # indices of "fixed" variables 

766 i_nf = np.logical_not(i_f) # indices of "not fixed" variables 

767 

768 # test_bounds_equal_but_infeasible 

769 if np.all(i_f): # if bounds define solution, check for consistency 

770 residual = b_eq - A_eq.dot(lb) 

771 slack = b_ub - A_ub.dot(lb) 

772 if ((A_ub.size > 0 and np.any(slack < 0)) or 

773 (A_eq.size > 0 and not np.allclose(residual, 0))): 

774 status = 2 

775 message = ("The problem is (trivially) infeasible because the " 

776 "bounds fix all variables to values inconsistent with " 

777 "the constraints") 

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 

782 ub_mod = ub 

783 lb_mod = lb 

784 if np.any(i_f): 

785 c0 += c[i_f].dot(lb[i_f]) 

786 b_eq = b_eq - A_eq[:, i_f].dot(lb[i_f]) 

787 b_ub = b_ub - A_ub[:, i_f].dot(lb[i_f]) 

788 c = c[i_nf] 

789 x_undo = lb[i_f] # not x[i_f], x is just zeroes 

790 x = x[i_nf] 

791 # user guess x0 stays separate from presolve solution x 

792 if x0 is not None: 

793 x0 = x0[i_nf] 

794 A_eq = A_eq[:, i_nf] 

795 A_ub = A_ub[:, i_nf] 

796 # modify bounds 

797 lb_mod = lb[i_nf] 

798 ub_mod = ub[i_nf] 

799 

800 def rev(x_mod): 

801 # Function to restore x: insert x_undo into x_mod. 

802 # When elements have been removed at positions k1, k2, k3, ... 

803 # then these must be replaced at (after) positions k1-1, k2-2, 

804 # k3-3, ... in the modified array to recreate the original 

805 i = np.flatnonzero(i_f) 

806 # Number of variables to restore 

807 N = len(i) 

808 index_offset = np.arange(N) 

809 # Create insert indices 

810 insert_indices = i - index_offset 

811 x_rev = np.insert(x_mod.astype(float), insert_indices, x_undo) 

812 return x_rev 

813 

814 # Use revstack as a list of functions, currently just this one. 

815 revstack.append(rev) 

816 

817 # no constraints indicates that problem is trivial 

818 if A_eq.size == 0 and A_ub.size == 0: 

819 b_eq = np.array([]) 

820 b_ub = np.array([]) 

821 # test_empty_constraint_1 

822 if c.size == 0: 

823 status = 0 

824 message = ("The solution was determined in presolve as there are " 

825 "no non-trivial constraints.") 

826 elif (np.any(np.logical_and(c < 0, ub_mod == np.inf)) or 

827 np.any(np.logical_and(c > 0, lb_mod == -np.inf))): 

828 # test_no_constraints() 

829 # test_unbounded_no_nontrivial_constraints_1 

830 # test_unbounded_no_nontrivial_constraints_2 

831 status = 3 

832 message = ("The problem is (trivially) unbounded " 

833 "because there are no non-trivial constraints and " 

834 "a) at least one decision variable is unbounded " 

835 "above and its corresponding cost is negative, or " 

836 "b) at least one decision variable is unbounded below " 

837 "and its corresponding cost is positive. ") 

838 else: # test_empty_constraint_2 

839 status = 0 

840 message = ("The solution was determined in presolve as there are " 

841 "no non-trivial constraints.") 

842 complete = True 

843 x[c < 0] = ub_mod[c < 0] 

844 x[c > 0] = lb_mod[c > 0] 

845 # where c is zero, set x to a finite bound or zero 

846 x_zero_c = ub_mod[c == 0] 

847 x_zero_c[np.isinf(x_zero_c)] = ub_mod[c == 0][np.isinf(x_zero_c)] 

848 x_zero_c[np.isinf(x_zero_c)] = 0 

849 x[c == 0] = x_zero_c 

850 # if this is not the last step of presolve, should convert bounds back 

851 # to array and return here 

852 

853 # Convert modified lb and ub back into N x 2 bounds 

854 bounds = np.hstack((lb_mod[:, np.newaxis], ub_mod[:, np.newaxis])) 

855 

856 # remove redundant (linearly dependent) rows from equality constraints 

857 n_rows_A = A_eq.shape[0] 

858 redundancy_warning = ("A_eq does not appear to be of full row rank. To " 

859 "improve performance, check the problem formulation " 

860 "for redundant equality constraints.") 

861 if (sps.issparse(A_eq)): 

862 if rr and A_eq.size > 0: # TODO: Fast sparse rank check? 

863 rr_res = _remove_redundancy_pivot_sparse(A_eq, b_eq) 

864 A_eq, b_eq, status, message = rr_res 

865 if A_eq.shape[0] < n_rows_A: 

866 warn(redundancy_warning, OptimizeWarning, stacklevel=1) 

867 if status != 0: 

868 complete = True 

869 return (_LPProblem(c, A_ub, b_ub, A_eq, b_eq, bounds, x0), 

870 c0, x, revstack, complete, status, message) 

871 

872 # This is a wild guess for which redundancy removal algorithm will be 

873 # faster. More testing would be good. 

874 small_nullspace = 5 

875 if rr and A_eq.size > 0: 

876 try: # TODO: use results of first SVD in _remove_redundancy_svd 

877 rank = np.linalg.matrix_rank(A_eq) 

878 # oh well, we'll have to go with _remove_redundancy_pivot_dense 

879 except Exception: 

880 rank = 0 

881 if rr and A_eq.size > 0 and rank < A_eq.shape[0]: 

882 warn(redundancy_warning, OptimizeWarning, stacklevel=3) 

883 dim_row_nullspace = A_eq.shape[0]-rank 

884 if rr_method is None: 

885 if dim_row_nullspace <= small_nullspace: 

886 rr_res = _remove_redundancy_svd(A_eq, b_eq) 

887 A_eq, b_eq, status, message = rr_res 

888 if dim_row_nullspace > small_nullspace or status == 4: 

889 rr_res = _remove_redundancy_pivot_dense(A_eq, b_eq) 

890 A_eq, b_eq, status, message = rr_res 

891 

892 else: 

893 rr_method = rr_method.lower() 

894 if rr_method == "svd": 

895 rr_res = _remove_redundancy_svd(A_eq, b_eq) 

896 A_eq, b_eq, status, message = rr_res 

897 elif rr_method == "pivot": 

898 rr_res = _remove_redundancy_pivot_dense(A_eq, b_eq) 

899 A_eq, b_eq, status, message = rr_res 

900 elif rr_method == "id": 

901 rr_res = _remove_redundancy_id(A_eq, b_eq, rank) 

902 A_eq, b_eq, status, message = rr_res 

903 else: # shouldn't get here; option validity checked above 

904 pass 

905 if A_eq.shape[0] < rank: 

906 message = ("Due to numerical issues, redundant equality " 

907 "constraints could not be removed automatically. " 

908 "Try providing your constraint matrices as sparse " 

909 "matrices to activate sparse presolve, try turning " 

910 "off redundancy removal, or try turning off presolve " 

911 "altogether.") 

912 status = 4 

913 if status != 0: 

914 complete = True 

915 return (_LPProblem(c, A_ub, b_ub, A_eq, b_eq, bounds, x0), 

916 c0, x, revstack, complete, status, message) 

917 

918 

919def _parse_linprog(lp, options, meth): 

920 """ 

921 Parse the provided linear programming problem 

922 

923 ``_parse_linprog`` employs two main steps ``_check_sparse_inputs`` and 

924 ``_clean_inputs``. ``_check_sparse_inputs`` checks for sparsity in the 

925 provided constraints (``A_ub`` and ``A_eq) and if these match the provided 

926 sparsity optional values. 

927 

928 ``_clean inputs`` checks of the provided inputs. If no violations are 

929 identified the objective vector, upper bound constraints, equality 

930 constraints, and simple bounds are returned in the expected format. 

931 

932 Parameters 

933 ---------- 

934 lp : A `scipy.optimize._linprog_util._LPProblem` consisting of the following fields: 

935 

936 c : 1D array 

937 The coefficients of the linear objective function to be minimized. 

938 A_ub : 2D array, optional 

939 The inequality constraint matrix. Each row of ``A_ub`` specifies the 

940 coefficients of a linear inequality constraint on ``x``. 

941 b_ub : 1D array, optional 

942 The inequality constraint vector. Each element represents an 

943 upper bound on the corresponding value of ``A_ub @ x``. 

944 A_eq : 2D array, optional 

945 The equality constraint matrix. Each row of ``A_eq`` specifies the 

946 coefficients of a linear equality constraint on ``x``. 

947 b_eq : 1D array, optional 

948 The equality constraint vector. Each element of ``A_eq @ x`` must equal 

949 the corresponding element of ``b_eq``. 

950 bounds : various valid formats, optional 

951 The bounds of ``x``, as ``min`` and ``max`` pairs. 

952 If bounds are specified for all N variables separately, valid formats are: 

953 * a 2D array (2 x N or N x 2); 

954 * a sequence of N sequences, each with 2 values. 

955 If all variables have the same bounds, a single pair of values can 

956 be specified. Valid formats are: 

957 * a sequence with 2 scalar values; 

958 * a sequence with a single element containing 2 scalar values. 

959 If all variables have a lower bound of 0 and no upper bound, the bounds 

960 parameter can be omitted (or given as None). 

961 x0 : 1D array, optional 

962 Guess values of the decision variables, which will be refined by 

963 the optimization algorithm. This argument is currently used only by the 

964 'revised simplex' method, and can only be used if `x0` represents a 

965 basic feasible solution. 

966 

967 options : dict 

968 A dictionary of solver options. All methods accept the following 

969 generic options: 

970 

971 maxiter : int 

972 Maximum number of iterations to perform. 

973 disp : bool 

974 Set to True to print convergence messages. 

975 

976 For method-specific options, see :func:`show_options('linprog')`. 

977 

978 Returns 

979 ------- 

980 lp : A `scipy.optimize._linprog_util._LPProblem` consisting of the following fields: 

981 

982 c : 1D array 

983 The coefficients of the linear objective function to be minimized. 

984 A_ub : 2D array, optional 

985 The inequality constraint matrix. Each row of ``A_ub`` specifies the 

986 coefficients of a linear inequality constraint on ``x``. 

987 b_ub : 1D array, optional 

988 The inequality constraint vector. Each element represents an 

989 upper bound on the corresponding value of ``A_ub @ x``. 

990 A_eq : 2D array, optional 

991 The equality constraint matrix. Each row of ``A_eq`` specifies the 

992 coefficients of a linear equality constraint on ``x``. 

993 b_eq : 1D array, optional 

994 The equality constraint vector. Each element of ``A_eq @ x`` must equal 

995 the corresponding element of ``b_eq``. 

996 bounds : 2D array 

997 The bounds of ``x``, as ``min`` and ``max`` pairs, one for each of the N 

998 elements of ``x``. The N x 2 array contains lower bounds in the first 

999 column and upper bounds in the 2nd. Unbounded variables have lower 

1000 bound -np.inf and/or upper bound np.inf. 

1001 x0 : 1D array, optional 

1002 Guess values of the decision variables, which will be refined by 

1003 the optimization algorithm. This argument is currently used only by the 

1004 'revised simplex' method, and can only be used if `x0` represents a 

1005 basic feasible solution. 

1006 

1007 options : dict, optional 

1008 A dictionary of solver options. All methods accept the following 

1009 generic options: 

1010 

1011 maxiter : int 

1012 Maximum number of iterations to perform. 

1013 disp : bool 

1014 Set to True to print convergence messages. 

1015 

1016 For method-specific options, see :func:`show_options('linprog')`. 

1017 

1018 """ 

1019 if options is None: 

1020 options = {} 

1021 

1022 solver_options = {k: v for k, v in options.items()} 

1023 solver_options, A_ub, A_eq = _check_sparse_inputs(solver_options, meth, 

1024 lp.A_ub, lp.A_eq) 

1025 # Convert lists to numpy arrays, etc... 

1026 lp = _clean_inputs(lp._replace(A_ub=A_ub, A_eq=A_eq)) 

1027 return lp, solver_options 

1028 

1029 

1030def _get_Abc(lp, c0): 

1031 """ 

1032 Given a linear programming problem of the form: 

1033 

1034 Minimize:: 

1035 

1036 c @ x 

1037 

1038 Subject to:: 

1039 

1040 A_ub @ x <= b_ub 

1041 A_eq @ x == b_eq 

1042 lb <= x <= ub 

1043 

1044 where ``lb = 0`` and ``ub = None`` unless set in ``bounds``. 

1045 

1046 Return the problem in standard form: 

1047 

1048 Minimize:: 

1049 

1050 c @ x 

1051 

1052 Subject to:: 

1053 

1054 A @ x == b 

1055 x >= 0 

1056 

1057 by adding slack variables and making variable substitutions as necessary. 

1058 

1059 Parameters 

1060 ---------- 

1061 lp : A `scipy.optimize._linprog_util._LPProblem` consisting of the following fields: 

1062 

1063 c : 1D array 

1064 The coefficients of the linear objective function to be minimized. 

1065 A_ub : 2D array, optional 

1066 The inequality constraint matrix. Each row of ``A_ub`` specifies the 

1067 coefficients of a linear inequality constraint on ``x``. 

1068 b_ub : 1D array, optional 

1069 The inequality constraint vector. Each element represents an 

1070 upper bound on the corresponding value of ``A_ub @ x``. 

1071 A_eq : 2D array, optional 

1072 The equality constraint matrix. Each row of ``A_eq`` specifies the 

1073 coefficients of a linear equality constraint on ``x``. 

1074 b_eq : 1D array, optional 

1075 The equality constraint vector. Each element of ``A_eq @ x`` must equal 

1076 the corresponding element of ``b_eq``. 

1077 bounds : 2D array 

1078 The bounds of ``x``, lower bounds in the 1st column, upper 

1079 bounds in the 2nd column. The bounds are possibly tightened 

1080 by the presolve procedure. 

1081 x0 : 1D array, optional 

1082 Guess values of the decision variables, which will be refined by 

1083 the optimization algorithm. This argument is currently used only by the 

1084 'revised simplex' method, and can only be used if `x0` represents a 

1085 basic feasible solution. 

1086 

1087 c0 : float 

1088 Constant term in objective function due to fixed (and eliminated) 

1089 variables. 

1090 

1091 Returns 

1092 ------- 

1093 A : 2-D array 

1094 2-D array such that ``A`` @ ``x``, gives the values of the equality 

1095 constraints at ``x``. 

1096 b : 1-D array 

1097 1-D array of values representing the RHS of each equality constraint 

1098 (row) in A (for standard form problem). 

1099 c : 1-D array 

1100 Coefficients of the linear objective function to be minimized (for 

1101 standard form problem). 

1102 c0 : float 

1103 Constant term in objective function due to fixed (and eliminated) 

1104 variables. 

1105 x0 : 1-D array 

1106 Starting values of the independent variables, which will be refined by 

1107 the optimization algorithm 

1108 

1109 References 

1110 ---------- 

1111 .. [9] Bertsimas, Dimitris, and J. Tsitsiklis. "Introduction to linear 

1112 programming." Athena Scientific 1 (1997): 997. 

1113 

1114 """ 

1115 c, A_ub, b_ub, A_eq, b_eq, bounds, x0, integrality = lp 

1116 

1117 if sps.issparse(A_eq): 

1118 sparse = True 

1119 A_eq = sps.csr_matrix(A_eq) 

1120 A_ub = sps.csr_matrix(A_ub) 

1121 

1122 def hstack(blocks): 

1123 return sps.hstack(blocks, format="csr") 

1124 

1125 def vstack(blocks): 

1126 return sps.vstack(blocks, format="csr") 

1127 

1128 zeros = sps.csr_matrix 

1129 eye = sps.eye 

1130 else: 

1131 sparse = False 

1132 hstack = np.hstack 

1133 vstack = np.vstack 

1134 zeros = np.zeros 

1135 eye = np.eye 

1136 

1137 # Variables lbs and ubs (see below) may be changed, which feeds back into 

1138 # bounds, so copy. 

1139 bounds = np.array(bounds, copy=True) 

1140 

1141 # modify problem such that all variables have only non-negativity bounds 

1142 lbs = bounds[:, 0] 

1143 ubs = bounds[:, 1] 

1144 m_ub, n_ub = A_ub.shape 

1145 

1146 lb_none = np.equal(lbs, -np.inf) 

1147 ub_none = np.equal(ubs, np.inf) 

1148 lb_some = np.logical_not(lb_none) 

1149 ub_some = np.logical_not(ub_none) 

1150 

1151 # unbounded below: substitute xi = -xi' (unbounded above) 

1152 # if -inf <= xi <= ub, then -ub <= -xi <= inf, so swap and invert bounds 

1153 l_nolb_someub = np.logical_and(lb_none, ub_some) 

1154 i_nolb = np.nonzero(l_nolb_someub)[0] 

1155 lbs[l_nolb_someub], ubs[l_nolb_someub] = ( 

1156 -ubs[l_nolb_someub], -lbs[l_nolb_someub]) 

1157 lb_none = np.equal(lbs, -np.inf) 

1158 ub_none = np.equal(ubs, np.inf) 

1159 lb_some = np.logical_not(lb_none) 

1160 ub_some = np.logical_not(ub_none) 

1161 c[i_nolb] *= -1 

1162 if x0 is not None: 

1163 x0[i_nolb] *= -1 

1164 if len(i_nolb) > 0: 

1165 if A_ub.shape[0] > 0: # sometimes needed for sparse arrays... weird 

1166 A_ub[:, i_nolb] *= -1 

1167 if A_eq.shape[0] > 0: 

1168 A_eq[:, i_nolb] *= -1 

1169 

1170 # upper bound: add inequality constraint 

1171 i_newub, = ub_some.nonzero() 

1172 ub_newub = ubs[ub_some] 

1173 n_bounds = len(i_newub) 

1174 if n_bounds > 0: 

1175 shape = (n_bounds, A_ub.shape[1]) 

1176 if sparse: 

1177 idxs = (np.arange(n_bounds), i_newub) 

1178 A_ub = vstack((A_ub, sps.csr_matrix((np.ones(n_bounds), idxs), 

1179 shape=shape))) 

1180 else: 

1181 A_ub = vstack((A_ub, np.zeros(shape))) 

1182 A_ub[np.arange(m_ub, A_ub.shape[0]), i_newub] = 1 

1183 b_ub = np.concatenate((b_ub, np.zeros(n_bounds))) 

1184 b_ub[m_ub:] = ub_newub 

1185 

1186 A1 = vstack((A_ub, A_eq)) 

1187 b = np.concatenate((b_ub, b_eq)) 

1188 c = np.concatenate((c, np.zeros((A_ub.shape[0],)))) 

1189 if x0 is not None: 

1190 x0 = np.concatenate((x0, np.zeros((A_ub.shape[0],)))) 

1191 # unbounded: substitute xi = xi+ + xi- 

1192 l_free = np.logical_and(lb_none, ub_none) 

1193 i_free = np.nonzero(l_free)[0] 

1194 n_free = len(i_free) 

1195 c = np.concatenate((c, np.zeros(n_free))) 

1196 if x0 is not None: 

1197 x0 = np.concatenate((x0, np.zeros(n_free))) 

1198 A1 = hstack((A1[:, :n_ub], -A1[:, i_free])) 

1199 c[n_ub:n_ub+n_free] = -c[i_free] 

1200 if x0 is not None: 

1201 i_free_neg = x0[i_free] < 0 

1202 x0[np.arange(n_ub, A1.shape[1])[i_free_neg]] = -x0[i_free[i_free_neg]] 

1203 x0[i_free[i_free_neg]] = 0 

1204 

1205 # add slack variables 

1206 A2 = vstack([eye(A_ub.shape[0]), zeros((A_eq.shape[0], A_ub.shape[0]))]) 

1207 

1208 A = hstack([A1, A2]) 

1209 

1210 # lower bound: substitute xi = xi' + lb 

1211 # now there is a constant term in objective 

1212 i_shift = np.nonzero(lb_some)[0] 

1213 lb_shift = lbs[lb_some].astype(float) 

1214 c0 += np.sum(lb_shift * c[i_shift]) 

1215 if sparse: 

1216 b = b.reshape(-1, 1) 

1217 A = A.tocsc() 

1218 b -= (A[:, i_shift] * sps.diags(lb_shift)).sum(axis=1) 

1219 b = b.ravel() 

1220 else: 

1221 b -= (A[:, i_shift] * lb_shift).sum(axis=1) 

1222 if x0 is not None: 

1223 x0[i_shift] -= lb_shift 

1224 

1225 return A, b, c, c0, x0 

1226 

1227 

1228def _round_to_power_of_two(x): 

1229 """ 

1230 Round elements of the array to the nearest power of two. 

1231 """ 

1232 return 2**np.around(np.log2(x)) 

1233 

1234 

1235def _autoscale(A, b, c, x0): 

1236 """ 

1237 Scales the problem according to equilibration from [12]. 

1238 Also normalizes the right hand side vector by its maximum element. 

1239 """ 

1240 m, n = A.shape 

1241 

1242 C = 1 

1243 R = 1 

1244 

1245 if A.size > 0: 

1246 

1247 R = np.max(np.abs(A), axis=1) 

1248 if sps.issparse(A): 

1249 R = R.toarray().flatten() 

1250 R[R == 0] = 1 

1251 R = 1/_round_to_power_of_two(R) 

1252 A = sps.diags(R)*A if sps.issparse(A) else A*R.reshape(m, 1) 

1253 b = b*R 

1254 

1255 C = np.max(np.abs(A), axis=0) 

1256 if sps.issparse(A): 

1257 C = C.toarray().flatten() 

1258 C[C == 0] = 1 

1259 C = 1/_round_to_power_of_two(C) 

1260 A = A*sps.diags(C) if sps.issparse(A) else A*C 

1261 c = c*C 

1262 

1263 b_scale = np.max(np.abs(b)) if b.size > 0 else 1 

1264 if b_scale == 0: 

1265 b_scale = 1. 

1266 b = b/b_scale 

1267 

1268 if x0 is not None: 

1269 x0 = x0/b_scale*(1/C) 

1270 return A, b, c, x0, C, b_scale 

1271 

1272 

1273def _unscale(x, C, b_scale): 

1274 """ 

1275 Converts solution to _autoscale problem -> solution to original problem. 

1276 """ 

1277 

1278 try: 

1279 n = len(C) 

1280 # fails if sparse or scalar; that's OK. 

1281 # this is only needed for original simplex (never sparse) 

1282 except TypeError: 

1283 n = len(x) 

1284 

1285 return x[:n]*b_scale*C 

1286 

1287 

1288def _display_summary(message, status, fun, iteration): 

1289 """ 

1290 Print the termination summary of the linear program 

1291 

1292 Parameters 

1293 ---------- 

1294 message : str 

1295 A string descriptor of the exit status of the optimization. 

1296 status : int 

1297 An integer representing the exit status of the optimization:: 

1298 

1299 0 : Optimization terminated successfully 

1300 1 : Iteration limit reached 

1301 2 : Problem appears to be infeasible 

1302 3 : Problem appears to be unbounded 

1303 4 : Serious numerical difficulties encountered 

1304 

1305 fun : float 

1306 Value of the objective function. 

1307 iteration : iteration 

1308 The number of iterations performed. 

1309 """ 

1310 print(message) 

1311 if status in (0, 1): 

1312 print(" Current function value: {0: <12.6f}".format(fun)) 

1313 print(" Iterations: {0:d}".format(iteration)) 

1314 

1315 

1316def _postsolve(x, postsolve_args, complete=False): 

1317 """ 

1318 Given solution x to presolved, standard form linear program x, add 

1319 fixed variables back into the problem and undo the variable substitutions 

1320 to get solution to original linear program. Also, calculate the objective 

1321 function value, slack in original upper bound constraints, and residuals 

1322 in original equality constraints. 

1323 

1324 Parameters 

1325 ---------- 

1326 x : 1-D array 

1327 Solution vector to the standard-form problem. 

1328 postsolve_args : tuple 

1329 Data needed by _postsolve to convert the solution to the standard-form 

1330 problem into the solution to the original problem, including: 

1331 

1332 lp : A `scipy.optimize._linprog_util._LPProblem` consisting of the following fields: 

1333 

1334 c : 1D array 

1335 The coefficients of the linear objective function to be minimized. 

1336 A_ub : 2D array, optional 

1337 The inequality constraint matrix. Each row of ``A_ub`` specifies the 

1338 coefficients of a linear inequality constraint on ``x``. 

1339 b_ub : 1D array, optional 

1340 The inequality constraint vector. Each element represents an 

1341 upper bound on the corresponding value of ``A_ub @ x``. 

1342 A_eq : 2D array, optional 

1343 The equality constraint matrix. Each row of ``A_eq`` specifies the 

1344 coefficients of a linear equality constraint on ``x``. 

1345 b_eq : 1D array, optional 

1346 The equality constraint vector. Each element of ``A_eq @ x`` must equal 

1347 the corresponding element of ``b_eq``. 

1348 bounds : 2D array 

1349 The bounds of ``x``, lower bounds in the 1st column, upper 

1350 bounds in the 2nd column. The bounds are possibly tightened 

1351 by the presolve procedure. 

1352 x0 : 1D array, optional 

1353 Guess values of the decision variables, which will be refined by 

1354 the optimization algorithm. This argument is currently used only by the 

1355 'revised simplex' method, and can only be used if `x0` represents a 

1356 basic feasible solution. 

1357 

1358 revstack: list of functions 

1359 the functions in the list reverse the operations of _presolve() 

1360 the function signature is x_org = f(x_mod), where x_mod is the result 

1361 of a presolve step and x_org the value at the start of the step 

1362 complete : bool 

1363 Whether the solution is was determined in presolve (``True`` if so) 

1364 

1365 Returns 

1366 ------- 

1367 x : 1-D array 

1368 Solution vector to original linear programming problem 

1369 fun: float 

1370 optimal objective value for original problem 

1371 slack : 1-D array 

1372 The (non-negative) slack in the upper bound constraints, that is, 

1373 ``b_ub - A_ub @ x`` 

1374 con : 1-D array 

1375 The (nominally zero) residuals of the equality constraints, that is, 

1376 ``b - A_eq @ x`` 

1377 """ 

1378 # note that all the inputs are the ORIGINAL, unmodified versions 

1379 # no rows, columns have been removed 

1380 

1381 c, A_ub, b_ub, A_eq, b_eq, bounds, x0, integrality = postsolve_args[0] 

1382 revstack, C, b_scale = postsolve_args[1:] 

1383 

1384 x = _unscale(x, C, b_scale) 

1385 

1386 # Undo variable substitutions of _get_Abc() 

1387 # if "complete", problem was solved in presolve; don't do anything here 

1388 n_x = bounds.shape[0] 

1389 if not complete and bounds is not None: # bounds are never none, probably 

1390 n_unbounded = 0 

1391 for i, bi in enumerate(bounds): 

1392 lbi = bi[0] 

1393 ubi = bi[1] 

1394 if lbi == -np.inf and ubi == np.inf: 

1395 n_unbounded += 1 

1396 x[i] = x[i] - x[n_x + n_unbounded - 1] 

1397 else: 

1398 if lbi == -np.inf: 

1399 x[i] = ubi - x[i] 

1400 else: 

1401 x[i] += lbi 

1402 # all the rest of the variables were artificial 

1403 x = x[:n_x] 

1404 

1405 # If there were variables removed from the problem, add them back into the 

1406 # solution vector 

1407 # Apply the functions in revstack (reverse direction) 

1408 for rev in reversed(revstack): 

1409 x = rev(x) 

1410 

1411 fun = x.dot(c) 

1412 slack = b_ub - A_ub.dot(x) # report slack for ORIGINAL UB constraints 

1413 # report residuals of ORIGINAL EQ constraints 

1414 con = b_eq - A_eq.dot(x) 

1415 

1416 return x, fun, slack, con 

1417 

1418 

1419def _check_result(x, fun, status, slack, con, bounds, tol, message): 

1420 """ 

1421 Check the validity of the provided solution. 

1422 

1423 A valid (optimal) solution satisfies all bounds, all slack variables are 

1424 negative and all equality constraint residuals are strictly non-zero. 

1425 Further, the lower-bounds, upper-bounds, slack and residuals contain 

1426 no nan values. 

1427 

1428 Parameters 

1429 ---------- 

1430 x : 1-D array 

1431 Solution vector to original linear programming problem 

1432 fun: float 

1433 optimal objective value for original problem 

1434 status : int 

1435 An integer representing the exit status of the optimization:: 

1436 

1437 0 : Optimization terminated successfully 

1438 1 : Iteration limit reached 

1439 2 : Problem appears to be infeasible 

1440 3 : Problem appears to be unbounded 

1441 4 : Serious numerical difficulties encountered 

1442 

1443 slack : 1-D array 

1444 The (non-negative) slack in the upper bound constraints, that is, 

1445 ``b_ub - A_ub @ x`` 

1446 con : 1-D array 

1447 The (nominally zero) residuals of the equality constraints, that is, 

1448 ``b - A_eq @ x`` 

1449 bounds : 2D array 

1450 The bounds on the original variables ``x`` 

1451 message : str 

1452 A string descriptor of the exit status of the optimization. 

1453 tol : float 

1454 Termination tolerance; see [1]_ Section 4.5. 

1455 

1456 Returns 

1457 ------- 

1458 status : int 

1459 An integer representing the exit status of the optimization:: 

1460 

1461 0 : Optimization terminated successfully 

1462 1 : Iteration limit reached 

1463 2 : Problem appears to be infeasible 

1464 3 : Problem appears to be unbounded 

1465 4 : Serious numerical difficulties encountered 

1466 

1467 message : str 

1468 A string descriptor of the exit status of the optimization. 

1469 """ 

1470 # Somewhat arbitrary 

1471 tol = np.sqrt(tol) * 10 

1472 

1473 if x is None: 

1474 # HiGHS does not provide x if infeasible/unbounded 

1475 if status == 0: # Observed with HiGHS Simplex Primal 

1476 status = 4 

1477 message = ("The solver did not provide a solution nor did it " 

1478 "report a failure. Please submit a bug report.") 

1479 return status, message 

1480 

1481 contains_nans = ( 

1482 np.isnan(x).any() 

1483 or np.isnan(fun) 

1484 or np.isnan(slack).any() 

1485 or np.isnan(con).any() 

1486 ) 

1487 

1488 if contains_nans: 

1489 is_feasible = False 

1490 else: 

1491 invalid_bounds = (x < bounds[:, 0] - tol).any() or (x > bounds[:, 1] + tol).any() 

1492 invalid_slack = status != 3 and (slack < -tol).any() 

1493 invalid_con = status != 3 and (np.abs(con) > tol).any() 

1494 is_feasible = not (invalid_bounds or invalid_slack or invalid_con) 

1495 

1496 if status == 0 and not is_feasible: 

1497 status = 4 

1498 message = ("The solution does not satisfy the constraints within the " 

1499 "required tolerance of " + "{:.2E}".format(tol) + ", yet " 

1500 "no errors were raised and there is no certificate of " 

1501 "infeasibility or unboundedness. Check whether " 

1502 "the slack and constraint residuals are acceptable; " 

1503 "if not, consider enabling presolve, adjusting the " 

1504 "tolerance option(s), and/or using a different method. " 

1505 "Please consider submitting a bug report.") 

1506 elif status == 2 and is_feasible: 

1507 # Occurs if the simplex method exits after phase one with a very 

1508 # nearly basic feasible solution. Postsolving can make the solution 

1509 # basic, however, this solution is NOT optimal 

1510 status = 4 

1511 message = ("The solution is feasible, but the solver did not report " 

1512 "that the solution was optimal. Please try a different " 

1513 "method.") 

1514 

1515 return status, message