1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 2 /* */ 3 /* This file is part of the class library */ 4 /* SoPlex --- the Sequential object-oriented simPlex. */ 5 /* */ 6 /* Copyright (c) 1996-2023 Zuse Institute Berlin (ZIB) */ 7 /* */ 8 /* Licensed under the Apache License, Version 2.0 (the "License"); */ 9 /* you may not use this file except in compliance with the License. */ 10 /* You may obtain a copy of the License at */ 11 /* */ 12 /* http://www.apache.org/licenses/LICENSE-2.0 */ 13 /* */ 14 /* Unless required by applicable law or agreed to in writing, software */ 15 /* distributed under the License is distributed on an "AS IS" BASIS, */ 16 /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ 17 /* See the License for the specific language governing permissions and */ 18 /* limitations under the License. */ 19 /* */ 20 /* You should have received a copy of the Apache-2.0 license */ 21 /* along with SoPlex; see the file LICENSE. If not email to soplex@zib.de. */ 22 /* */ 23 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 24 25 /**@file spxlpbase.h 26 * @brief Saving LPs in a form suitable for SoPlex. 27 */ 28 #ifndef _SPXLPBASE_H_ 29 #define _SPXLPBASE_H_ 30 31 /* undefine SOPLEX_DEBUG flag from including files; if SOPLEX_DEBUG should be defined in this file, do so below */ 32 #ifdef SOPLEX_DEBUG 33 #define SOPLEX_DEBUG_SPXLPBASE 34 #undef SOPLEX_DEBUG 35 #endif 36 37 #include <assert.h> 38 #include <iostream> 39 #include <iomanip> 40 #include <typeinfo> 41 42 #include "soplex/spxdefines.h" 43 #include "soplex/basevectors.h" 44 #include "soplex/dataarray.h" 45 #include "soplex/datakey.h" 46 #include "soplex/spxid.h" 47 #include "soplex/lprowbase.h" 48 #include "soplex/lpcolbase.h" 49 #include "soplex/lprowsetbase.h" 50 #include "soplex/lpcolsetbase.h" 51 #include "soplex/nameset.h" 52 #include "soplex/didxset.h" 53 #include "soplex/spxfileio.h" 54 #include "soplex/spxscaler.h" 55 #include "soplex/rational.h" 56 57 namespace soplex 58 { 59 // Declarations to fix errors of the form "SPxMainSM is not a type" 60 template <class R> 61 class SPxSolverBase; 62 template <class R> 63 class SPxMainSM; 64 template <class R> 65 class SPxLPBase; 66 template <class R> 67 class SPxBasisBase; 68 69 template <class R> 70 class SPxEquiliSC; 71 template <class R> 72 class SPxLeastSqSC; 73 template <class R> 74 class SPxGeometSC; 75 template <class R> 76 class SPxMainSM; 77 78 79 /**@brief Saving LPs in a form suitable for SoPlex. 80 * @ingroup Algo 81 * 82 * Class SPxLPBase provides the data structures required for saving a linear program in the form 83 * \f[ 84 * \begin{array}{rl} 85 * \hbox{max} & c^T x \\ 86 * \hbox{s.t.} & l_r \le Ax \le u_r \\ 87 * & l_c \le x \le u_c 88 * \end{array} 89 * \f] 90 * suitable for solving with SoPlex. This includes: 91 * - SVSetBase%s for both columns and rows 92 * - objective Vector 93 * - upper and lower bound Vectors for variables (\f$l_c\f$ and \f$u_c\f$) 94 * - upper and lower bound Vectors for inequalities (\f$l_r\f$ and \f$u_r\f$) 95 * 96 * Note, that the optimization sense is not saved directly. Instead, the objective function are multiplied by -1 to 97 * transform the LP to our standard form maximizing the objective function. However, the sense of the loaded LP can be 98 * retrieved with method #spxSense(). 99 * 100 * Further, equality constraints are modeled by \f$l_r = u_r\f$. Analogously, fixed variables have \f$l_c = u_c\f$. 101 * 102 * #SPxLPBase%s are saved as an SVSet, both for columns and rows. Note that this is redundant but eases the access. 103 */ 104 105 106 template <class R> 107 class SPxLPBase : protected LPRowSetBase<R>, protected LPColSetBase<R> 108 { 109 template <class S> friend class SPxLPBase; 110 friend SPxBasisBase<R>; 111 friend SPxScaler<R>; 112 friend SPxEquiliSC<R>; 113 friend SPxLeastSqSC<R>; 114 friend SPxGeometSC<R>; 115 friend SPxMainSM<R>; 116 117 public: 118 119 // ------------------------------------------------------------------------------------------------------------------ 120 /**@name Types */ 121 ///@{ 122 123 /// Optimization sense. 124 enum SPxSense 125 { 126 MAXIMIZE = 1, 127 MINIMIZE = -1 128 }; 129 130 ///@} 131 132 private: 133 134 // ------------------------------------------------------------------------------------------------------------------ 135 /**@name Data */ 136 ///@{ 137 138 SPxSense thesense; ///< optimization sense. 139 R offset; ///< offset computed, e.g., in simplification step 140 bool _isScaled; ///< true, if scaling has been performed 141 SPxScaler<R>* 142 lp_scaler; ///< points to the scaler if the lp has been scaled, to nullptr otherwise 143 144 ///@} 145 146 public: 147 148 // message handler 149 SPxOut* spxout; 150 151 public: 152 153 void setOutstream(SPxOut& newOutstream) 154 { 155 spxout = &newOutstream; 156 } 157 158 // ------------------------------------------------------------------------------------------------------------------ 159 160 /// unscales the lp and clears basis 161 void unscaleLP(); 162 163 /**@name Inquiry */ 164 ///@{ 165 166 /// returns current tolerances 167 const std::shared_ptr<Tolerances> tolerances() const 168 { 169 return _tolerances; 170 } 171 172 /// set tolerances 173 virtual void setTolerances(std::shared_ptr<Tolerances> tolerances) 174 { 175 this->_tolerances = tolerances; 176 } 177 178 /// Returns true if and only if the LP is scaled 179 bool isScaled() const 180 { 181 return _isScaled; 182 } 183 184 /// set whether the LP is scaled or not 185 void setScalingInfo(bool scaled) 186 { 187 _isScaled = scaled; 188 } 189 190 /// Returns number of rows in LP. 191 int nRows() const 192 { 193 return LPRowSetBase<R>::num(); 194 } 195 196 /// Returns number of columns in LP. 197 int nCols() const 198 { 199 return LPColSetBase<R>::num(); 200 } 201 202 /// Returns number of nonzeros in LP. 203 int nNzos() const 204 { 205 206 int n = 0; 207 208 for(int i = 0; i < nCols(); ++i) 209 n += colVector(i).size(); 210 211 return n; 212 } 213 214 /// Absolute smallest non-zero element in (possibly scaled) LP. 215 virtual R minAbsNzo(bool unscaled = true) const; 216 217 /// Absolute biggest non-zero element in (in rational case possibly scaled) LP. 218 virtual R maxAbsNzo(bool unscaled = true) const; 219 220 /// Gets \p i 'th row. 221 void getRow(int i, LPRowBase<R>& row) const 222 { 223 row.setLhs(lhs(i)); 224 row.setRhs(rhs(i)); 225 row.setObj(rowObj(i)); 226 row.setRowVector(DSVectorBase<R>(rowVector(i))); 227 } 228 229 /// Gets row with identifier \p id. 230 void getRow(const SPxRowId& id, LPRowBase<R>& row) const 231 { 232 getRow(number(id), row); 233 } 234 235 /// Gets rows \p start, ... \p end. 236 void getRows(int start, int end, LPRowSetBase<R>& set) const 237 { 238 set.clear(); 239 240 for(int i = start; i <= end; i++) 241 set.add(lhs(i), rowVector(i), rhs(i), rowObj(i)); 242 } 243 244 /// Gets row vector of row \p i. 245 const SVectorBase<R>& rowVector(int i) const 246 { 247 return LPRowSetBase<R>::rowVector(i); 248 } 249 250 /// Gets row vector of row with identifier \p id. 251 const SVectorBase<R>& rowVector(const SPxRowId& id) const 252 { 253 return LPRowSetBase<R>::rowVector(id); 254 } 255 256 /// Gets unscaled row vector of row \p i. 257 void getRowVectorUnscaled(int i, DSVectorBase<R>& vec) const; 258 259 /// Returns right hand side vector. 260 const VectorBase<R>& rhs() const 261 { 262 return LPRowSetBase<R>::rhs(); 263 } 264 265 /// Returns right hand side of row number \p i. 266 const R& rhs(int i) const 267 { 268 return LPRowSetBase<R>::rhs(i); 269 } 270 271 272 /// Returns right hand side of row with identifier \p id. 273 const R& rhs(const SPxRowId& id) const 274 { 275 return LPRowSetBase<R>::rhs(id); 276 } 277 278 /// Gets (internal and possibly scaled) right hand side vector. 279 void getRhs(VectorBase<R>& vec) const 280 { 281 vec = LPRowSetBase<R>::rhs(); 282 } 283 284 /// Gets unscaled right hand side vector. 285 void getRhsUnscaled(VectorBase<R>& vec) const; 286 287 /// Returns unscaled right hand side of row number \p i. 288 R rhsUnscaled(int i) const; 289 290 /// Returns unscaled right hand side of row with identifier \p id. 291 R rhsUnscaled(const SPxRowId& id) const; 292 293 /// Returns left hand side vector. 294 const VectorBase<R>& lhs() const 295 { 296 return LPRowSetBase<R>::lhs(); 297 } 298 299 /// Returns left hand side of row number \p i. 300 const R& lhs(int i) const 301 { 302 return LPRowSetBase<R>::lhs(i); 303 } 304 305 /// Returns left hand side of row with identifier \p id. 306 const R& lhs(const SPxRowId& id) const 307 { 308 return LPRowSetBase<R>::lhs(id); 309 } 310 311 /// Gets row objective function vector. 312 void getRowObj(VectorBase<R>& prowobj) const 313 { 314 prowobj = LPRowSetBase<R>::obj(); 315 316 if(spxSense() == MINIMIZE) 317 prowobj *= -1.0; 318 } 319 320 /// 321 R rowObj(int i) const 322 { 323 if(spxSense() == MINIMIZE) 324 return -maxRowObj(i); 325 else 326 return maxRowObj(i); 327 } 328 329 /// Returns row objective function value of row with identifier \p id. 330 R rowObj(const SPxRowId& id) const 331 { 332 if(spxSense() == MINIMIZE) 333 return -maxRowObj(id); 334 else 335 return maxRowObj(id); 336 } 337 338 /// 339 const VectorBase<R>& maxRowObj() const 340 { 341 return LPRowSetBase<R>::obj(); 342 } 343 344 /// 345 const R& maxRowObj(int i) const 346 { 347 return LPRowSetBase<R>::obj(i); 348 } 349 350 /// Returns row objective function value of row with identifier \p id. 351 const R& maxRowObj(const SPxRowId& id) const 352 { 353 return LPRowSetBase<R>::obj(id); 354 } 355 356 /// Returns unscaled left hand side vector. 357 void getLhsUnscaled(VectorBase<R>& vec) const; 358 359 /// Returns unscaled left hand side of row number \p i. 360 R lhsUnscaled(int i) const; 361 362 /// Returns left hand side of row with identifier \p id. 363 R lhsUnscaled(const SPxRowId& id) const; 364 365 /// Returns the inequality type of the \p i'th LPRow. 366 typename LPRowBase<R>::Type rowType(int i) const 367 { 368 return LPRowSetBase<R>::type(i); 369 } 370 371 /// Returns the inequality type of the row with identifier \p key. 372 typename LPRowBase<R>::Type rowType(const SPxRowId& id) const 373 { 374 return LPRowSetBase<R>::type(id); 375 } 376 377 /// Gets \p i 'th column. 378 void getCol(int i, LPColBase<R>& col) const 379 { 380 col.setUpper(upper(i)); 381 col.setLower(lower(i)); 382 col.setObj(obj(i)); 383 col.setColVector(colVector(i)); 384 } 385 386 /// Gets column with identifier \p id. 387 void getCol(const SPxColId& id, LPColBase<R>& col) const 388 { 389 getCol(number(id), col); 390 } 391 392 /// Gets columns \p start, ..., \p end. 393 void getCols(int start, int end, LPColSetBase<R>& set) const 394 { 395 if(_isScaled) 396 { 397 LPColBase<R> lpcol; 398 399 for(int i = start; i <= end; i++) 400 { 401 getCol(i, lpcol); 402 set.add(lpcol); 403 } 404 405 } 406 else 407 { 408 set.clear(); 409 410 for(int i = start; i <= end; i++) 411 set.add(obj(i), lower(i), colVector(i), upper(i)); 412 } 413 } 414 415 /// Returns column vector of column \p i. 416 const SVectorBase<R>& colVector(int i) const 417 { 418 return LPColSetBase<R>::colVector(i); 419 } 420 421 /// Returns column vector of column with identifier \p id. 422 const SVectorBase<R>& colVector(const SPxColId& id) const 423 { 424 return LPColSetBase<R>::colVector(id); 425 } 426 427 /// Gets column vector of column \p i. 428 void getColVectorUnscaled(int i, DSVectorBase<R>& vec) const; 429 430 /// Gets column vector of column with identifier \p id. 431 void getColVectorUnscaled(const SPxColId& id, DSVectorBase<R>& vec) const; 432 433 /// Gets unscaled objective vector. 434 void getObjUnscaled(VectorBase<R>& pobj) const; 435 436 /// Gets objective vector. 437 void getObj(VectorBase<R>& pobj) const 438 { 439 pobj = LPColSetBase<R>::maxObj(); 440 441 if(spxSense() == MINIMIZE) 442 pobj *= -1.0; 443 } 444 445 /// Returns objective value of column \p i. 446 R obj(int i) const 447 { 448 R res = maxObj(i); 449 450 if(spxSense() == MINIMIZE) 451 res *= -1; 452 453 return res; 454 } 455 456 /// Returns objective value of column with identifier \p id. 457 R obj(const SPxColId& id) const 458 { 459 return obj(number(id)); 460 } 461 462 /// Returns unscaled objective value of column \p i. 463 R objUnscaled(int i) const; 464 465 /// Returns unscaled objective value of column with identifier \p id. 466 R objUnscaled(const SPxColId& id) const; 467 468 /// Returns objective vector for maximization problem. 469 /** Methods #maxObj() return the objective vector or its elements, after transformation to a maximization 470 * problem. Since this is how SPxLPBase internally stores any LP these methods are generally faster. The following 471 * condition holds: #obj() = #spxSense() * maxObj(). 472 */ 473 const VectorBase<R>& maxObj() const 474 { 475 return LPColSetBase<R>::maxObj(); 476 } 477 478 /// Returns objective value of column \p i for maximization problem. 479 const R& maxObj(int i) const 480 { 481 return LPColSetBase<R>::maxObj(i); 482 } 483 484 /// Returns objective value of column with identifier \p id for maximization problem. 485 const R& maxObj(const SPxColId& id) const 486 { 487 return maxObj(number(id)); 488 } 489 490 /// Returns unscaled objective vector for maximization problem. 491 void maxObjUnscaled(VectorBase<R>& vec) const; 492 493 /// Returns unscaled objective value of column \p i for maximization problem. 494 R maxObjUnscaled(int i) const; 495 496 /// Returns unscaled objective value of column with identifier \p id for maximization problem. 497 R maxObjUnscaled(const SPxColId& id) const; 498 499 /// Returns upper bound vector. 500 const VectorBase<R>& upper() const 501 { 502 return LPColSetBase<R>::upper(); 503 } 504 505 /// Returns upper bound of column \p i. 506 const R& upper(int i) const 507 { 508 return LPColSetBase<R>::upper(i); 509 } 510 511 /// Returns upper bound of column with identifier \p id. 512 const R& upper(const SPxColId& id) const 513 { 514 return LPColSetBase<R>::upper(id); 515 } 516 517 /// Gets unscaled upper bound vector 518 void getUpperUnscaled(VectorBase<R>& vec) const; 519 520 /// Returns unscaled upper bound of column \p i. 521 R upperUnscaled(int i) const; 522 523 /// Returns unscaled upper bound of column with identifier \p id. 524 R upperUnscaled(const SPxColId& id) const; 525 526 /// Returns (internal and possibly scaled) lower bound vector. 527 const VectorBase<R>& lower() const 528 { 529 return LPColSetBase<R>::lower(); 530 } 531 532 /// Returns (internal and possibly scaled) lower bound of column \p i. 533 const R& lower(int i) const 534 { 535 return LPColSetBase<R>::lower(i); 536 } 537 538 /// Returns (internal and possibly scaled) lower bound of column with identifier \p id. 539 const R& lower(const SPxColId& id) const 540 { 541 return LPColSetBase<R>::lower(id); 542 } 543 544 /// Gets unscaled lower bound vector. 545 void getLowerUnscaled(VectorBase<R>& vec) const; 546 547 /// Returns unscaled lower bound of column \p i. 548 R lowerUnscaled(int i) const; 549 550 /// Returns unscaled lower bound of column with identifier \p id. 551 R lowerUnscaled(const SPxColId& id) const; 552 553 /// Returns the optimization sense. 554 SPxSense spxSense() const 555 { 556 return thesense; 557 } 558 559 /// Returns the objective function value offset 560 const R& objOffset() const 561 { 562 return offset; 563 } 564 565 /// Returns the row number of the row with identifier \p id. 566 int number(const SPxRowId& id) const 567 { 568 return LPRowSetBase<R>::number(id); 569 } 570 571 /// Returns the column number of the column with identifier \p id. 572 int number(const SPxColId& id) const 573 { 574 return LPColSetBase<R>::number(id); 575 } 576 577 /// Returns the row or column number for identifier \p id. 578 int number(const SPxId& id) const 579 { 580 return (id.type() == SPxId::COL_ID) 581 ? LPColSetBase<R>::number(id) 582 : LPRowSetBase<R>::number(id); 583 } 584 585 /// Returns the row number of the row with identifier \p id. 586 bool has(const SPxRowId& id) const 587 { 588 return LPRowSetBase<R>::has(id); 589 } 590 591 /// Returns the column number of the column with identifier \p id. 592 bool has(const SPxColId& id) const 593 { 594 return LPColSetBase<R>::has(id); 595 } 596 597 /// Returns the row or column number for identifier \p id. 598 bool has(const SPxId& id) const 599 { 600 return (id.type() == SPxId::COL_ID) 601 ? LPColSetBase<R>::has(id) 602 : LPRowSetBase<R>::has(id); 603 } 604 605 /// Returns the row identifier for row \p n. 606 SPxRowId rId(int n) const 607 { 608 return SPxRowId(LPRowSetBase<R>::key(n)); 609 } 610 611 /// Returns the column identifier for column \p n. 612 SPxColId cId(int n) const 613 { 614 return SPxColId(LPColSetBase<R>::key(n)); 615 } 616 617 ///@} 618 619 // ------------------------------------------------------------------------------------------------------------------ 620 /**@name Extension */ 621 ///@{ 622 623 /// 624 virtual void addRow(const LPRowBase<R>& row, bool scale = false) 625 { 626 doAddRow(row, scale); 627 } 628 629 /// 630 virtual void addRow(const R& lhsValue, const SVectorBase<R>& rowVec, const R& rhsValue, 631 bool scale = false) 632 { 633 doAddRow(lhsValue, rowVec, rhsValue, scale); 634 } 635 636 /// 637 template < class S > 638 void addRow(const S* lhsValue, const S* rowValues, const int* rowIndices, int rowSize, 639 const S* rhsValue) 640 { 641 assert(lhsValue != 0); 642 assert(rowSize <= 0 || rowValues != 0); 643 assert(rowSize <= 0 || rowIndices != 0); 644 assert(rhsValue != 0); 645 646 int idx = nRows(); 647 int oldColNumber = nCols(); 648 649 LPRowSetBase<R>::add(lhsValue, rowValues, rowIndices, rowSize, rhsValue); 650 651 // now insert nonzeros to column file also 652 for(int j = rowSize - 1; j >= 0; --j) 653 { 654 const S& val = rowValues[j]; 655 int i = rowIndices[j]; 656 657 // create new columns if required 658 if(i >= nCols()) 659 { 660 LPColBase<R> empty; 661 662 for(int k = nCols(); k <= i; ++k) 663 LPColSetBase<R>::add(empty); 664 } 665 666 assert(i < nCols()); 667 LPColSetBase<R>::add2(i, 1, &idx, &val); 668 } 669 670 addedRows(1); 671 addedCols(nCols() - oldColNumber); 672 } 673 674 /// Adds \p row to LPRowSetBase. 675 virtual void addRow(SPxRowId& id, const LPRowBase<R>& row, bool scale = false) 676 { 677 addRow(row, scale); 678 id = rId(nRows() - 1); 679 } 680 681 /// 682 virtual void addRows(const LPRowSetBase<R>& pset, bool scale = false) 683 { 684 doAddRows(pset, scale); 685 } 686 687 /// 688 template < class S > 689 void addRows(const S* lhsValues, const S* rowValues, const int* rowIndices, const int* rowStarts, 690 const int* rowLengths, const int numRows, const int numValues, const S* rhsValues) 691 { 692 assert(lhsValues != 0); 693 assert(numValues <= 0 || rowValues != 0); 694 assert(numValues <= 0 || rowIndices != 0); 695 assert(numValues <= 0 || rowStarts != 0); 696 assert(numValues <= 0 || rowLengths != 0); 697 assert(rhsValues != 0); 698 699 int i, j, k, idx; 700 SVectorBase<R>* col; 701 DataArray < int > newCols(nCols()); 702 int oldRowNumber = nRows(); 703 int oldColNumber = nCols(); 704 705 LPRowSetBase<R>::memRemax(oldRowNumber + numRows); 706 707 for(i = 0; i < numRows; i++) 708 { 709 assert(numValues <= 0 || rowStarts[i] + rowLengths[i] <= numValues); 710 711 if(numValues <= 0) 712 LPRowSetBase<R>::add(&(lhsValues[i]), (S*)0, (int*)0, 0, &(rhsValues[i])); 713 else 714 LPRowSetBase<R>::add(&(lhsValues[i]), &(rowValues[rowStarts[i]]), &(rowIndices[rowStarts[i]]), 715 rowLengths[i], &(rhsValues[i])); 716 } 717 718 assert(LPRowSetBase<R>::isConsistent()); 719 assert(LPColSetBase<R>::isConsistent()); 720 721 // count additional nonzeros per column 722 for(i = nCols() - 1; i >= 0; --i) 723 newCols[i] = 0; 724 725 if(numValues > 0) 726 { 727 for(i = 0; i < numRows; i++) 728 { 729 for(j = rowStarts[i]; j < rowStarts[i] + rowLengths[i]; j++) 730 { 731 ///@todo implement the addition of new columns as in doAddRows() 732 assert(rowIndices[j] >= 0); 733 assert(rowIndices[j] < oldColNumber); 734 newCols[rowIndices[j]]++; 735 } 736 } 737 } 738 739 // extend columns as required (backward because of memory efficiency reasons) 740 for(i = nCols() - 1; i >= 0; --i) 741 { 742 if(newCols[i] > 0) 743 { 744 int len = newCols[i] + colVector(i).size(); 745 LPColSetBase<R>::xtend(i, len); 746 747 /* preset the sizes: beware that this can irritate a consistency check call from xtend(). We need to set the 748 * sizes here, because a possible garbage collection called from xtend might destroy the sizes again. */ 749 colVector_w(i).set_size(len); 750 } 751 } 752 753 // insert new elements to column file 754 for(i = nRows() - 1; i >= oldRowNumber; --i) 755 { 756 const SVectorBase<R>& vec = rowVector(i); 757 758 for(j = vec.size() - 1; j >= 0; --j) 759 { 760 k = vec.index(j); 761 col = &colVector_w(k); 762 idx = col->size() - newCols[k]; 763 assert(newCols[k] > 0); 764 assert(idx >= 0); 765 newCols[k]--; 766 col->index(idx) = i; 767 col->value(idx) = vec.value(j); 768 } 769 } 770 771 #ifndef NDEBUG 772 773 for(i = 0; i < nCols(); ++i) 774 assert(newCols[i] == 0); 775 776 #endif 777 778 assert(SPxLPBase<R>::isConsistent()); 779 780 assert(numRows == nRows() - oldRowNumber); 781 addedRows(nRows() - oldRowNumber); 782 addedCols(nCols() - oldColNumber); 783 } 784 785 /// adds all LPRowBase%s of \p pset to LPRowSetBase. 786 virtual void addRows(SPxRowId id[], const LPRowSetBase<R>& set, bool scale = false) 787 { 788 int i = nRows(); 789 addRows(set, scale); 790 791 for(int j = 0; i < nRows(); ++i, ++j) 792 id[j] = rId(i); 793 } 794 795 /// 796 virtual void addCol(const LPColBase<R>& col, bool scale = false) 797 { 798 doAddCol(col, scale); 799 } 800 801 /// 802 virtual void addCol(const R& objValue, const R& lowerValue, const SVectorBase<R>& colVec, 803 const R& upperValue, bool scale = false) 804 { 805 doAddCol(objValue, lowerValue, colVec, upperValue, scale); 806 } 807 808 /// 809 template < class S > 810 void addCol(const S* objValue, const S* lowerValue, const S* colValues, const int* colIndices, 811 int colSize, const S* upperValue) 812 { 813 int idx = nCols(); 814 int oldRowNumber = nRows(); 815 816 LPColSetBase<R>::add(objValue, lowerValue, colValues, colIndices, colSize, upperValue); 817 818 if(thesense != MAXIMIZE) 819 LPColSetBase<R>::maxObj_w(idx) *= -1; 820 821 // now insert nonzeros to column file also 822 for(int j = colSize - 1; j >= 0; --j) 823 { 824 const S& val = colValues[j]; 825 int i = colIndices[j]; 826 827 // create new rows if required 828 if(i >= nRows()) 829 { 830 LPRowBase<R> empty; 831 832 for(int k = nRows(); k <= i; ++k) 833 LPRowSetBase<R>::add(empty); 834 } 835 836 assert(i < nRows()); 837 LPRowSetBase<R>::add2(i, 1, &idx, &val); 838 } 839 840 addedCols(1); 841 addedRows(nRows() - oldRowNumber); 842 } 843 844 /// Adds \p col to LPColSetVBase. 845 virtual void addCol(SPxColId& id, const LPColBase<R>& col, bool scale = false) 846 { 847 addCol(col, scale); 848 id = cId(nCols() - 1); 849 } 850 851 /// 852 virtual void addCols(const LPColSetBase<R>& pset, bool scale = false) 853 { 854 doAddCols(pset, scale); 855 } 856 857 /// 858 template < class S > 859 void addCols(const S* objValue, const S* lowerValues, const S* colValues, const int* colIndices, 860 const int* colStarts, const int* colLengths, const int numCols, const int numValues, 861 const S* upperValues) 862 { 863 assert(lowerValues != 0); 864 assert(numValues <= 0 || colValues != 0); 865 assert(numValues <= 0 || colIndices != 0); 866 assert(numValues <= 0 || colStarts != 0); 867 assert(numValues <= 0 || colLengths != 0); 868 assert(upperValues != 0); 869 870 int i, j, k, idx; 871 SVectorBase<R>* row; 872 DataArray < int > newRows(nRows()); 873 int oldColNumber = nCols(); 874 int oldRowNumber = nRows(); 875 idx = nCols(); 876 877 LPColSetBase<R>::memRemax(oldColNumber + numCols); 878 879 for(i = 0; i < numCols; i++) 880 { 881 assert(numValues <= 0 || colStarts[i] + colLengths[i] <= numValues); 882 883 if(numValues <= 0) 884 LPColSetBase<R>::add(&(objValue[i]), &(lowerValues[i]), (S*)0, (int*)0, 0, &(upperValues[i])); 885 else 886 LPColSetBase<R>::add(&(objValue[i]), &(lowerValues[i]), &(colValues[colStarts[i]]), 887 &(colIndices[colStarts[i]]), colLengths[i], &(upperValues[i])); 888 889 if(thesense != MAXIMIZE) 890 LPColSetBase<R>::maxObj_w(idx + i) *= -1; 891 } 892 893 assert(LPColSetBase<R>::isConsistent()); 894 assert(LPRowSetBase<R>::isConsistent()); 895 896 // count additional nonzeros per rows 897 for(i = nRows() - 1; i >= 0; --i) 898 newRows[i] = 0; 899 900 for(i = numValues - 1; i >= 0; --i) 901 { 902 ///@todo implement the addition of new rows as in doAddCols() 903 assert(colIndices[i] >= 0); 904 assert(colIndices[i] < oldRowNumber); 905 newRows[colIndices[i]]++; 906 } 907 908 // extend rows as required (backward because of memory efficiency reasons) 909 for(i = nRows() - 1; i >= 0; --i) 910 { 911 if(newRows[i] > 0) 912 { 913 int len = newRows[i] + rowVector(i).size(); 914 LPRowSetBase<R>::xtend(i, len); 915 916 /* preset the sizes: beware that this can irritate a consistency check call from xtend(). We need to set the 917 * sizes here, because a possible garbage collection called from xtend might destroy the sizes again. */ 918 rowVector_w(i).set_size(len); 919 } 920 } 921 922 // insert new elements to row file 923 for(i = nCols() - 1; i >= oldColNumber; --i) 924 { 925 const SVectorBase<R>& vec = colVector(i); 926 927 for(j = vec.size() - 1; j >= 0; --j) 928 { 929 k = vec.index(j); 930 row = &rowVector_w(k); 931 idx = row->size() - newRows[k]; 932 assert(newRows[k] > 0); 933 assert(idx >= 0); 934 newRows[k]--; 935 row->index(idx) = i; 936 row->value(idx) = vec.value(j); 937 } 938 } 939 940 #ifndef NDEBUG 941 942 for(i = 0; i < nRows(); ++i) 943 assert(newRows[i] == 0); 944 945 #endif 946 947 assert(SPxLPBase<R>::isConsistent()); 948 949 assert(numCols == nCols() - oldColNumber); 950 addedCols(nCols() - oldColNumber); 951 addedRows(nRows() - oldRowNumber); 952 } 953 954 /// Adds all LPColBase%s of \p set to LPColSetBase. 955 virtual void addCols(SPxColId id[], const LPColSetBase<R>& set, bool scale = false) 956 { 957 958 int i = nCols(); 959 addCols(set, scale); 960 961 for(int j = 0; i < nCols(); ++i, ++j) 962 id[j] = cId(i); 963 } 964 965 ///@} 966 967 // ------------------------------------------------------------------------------------------------------------------ 968 /**@name Shrinking */ 969 ///@{ 970 971 /// Removes \p i 'th row. 972 virtual void removeRow(int i) 973 { 974 if(i < 0) 975 return; 976 977 doRemoveRow(i); 978 } 979 980 /// Removes row with identifier \p id. 981 virtual void removeRow(SPxRowId id) 982 { 983 removeRow(number(id)); 984 } 985 986 /// Removes multiple rows. 987 /** This method removes all LPRowBase%s from the SPxLPBase with an index \p i such that \p perm[i] < 0. Upon 988 * completion, \p perm[i] >= 0 indicates the new index where the \p i'th LPRowBase<R> has been moved to due to this 989 * removal. Note that \p perm must point to an array of at least #nRows() ints. 990 */ 991 virtual void removeRows(int perm[]) 992 { 993 doRemoveRows(perm); 994 } 995 996 /// 997 virtual void removeRows(SPxRowId id[], int n, int perm[] = 0) 998 { 999 1000 if(perm == 0) 1001 { 1002 DataArray < int > p(nRows()); 1003 removeRows(id, n, p.get_ptr()); 1004 return; 1005 } 1006 1007 for(int i = nRows() - 1; i >= 0; --i) 1008 perm[i] = i; 1009 1010 while(n--) 1011 perm[number(id[n])] = -1; 1012 1013 removeRows(perm); 1014 } 1015 1016 /// Removes \p n LPRowBase%s. 1017 /** Removing multiple rows with one method invocation is available in two flavours. An array \p perm can be passed as 1018 * third argument or not. If given, \p perm must be an array at least of size #nRows(). It is used to return the 1019 * permutations resulting from this removal: \p perm[i] < 0 indicates, that the element to index \p i has been 1020 * removed. Otherwise, \p perm[i] is the new index of the element with index \p i before the removal. 1021 */ 1022 virtual void removeRows(int nums[], int n, int perm[] = 0) 1023 { 1024 1025 if(perm == 0) 1026 { 1027 DataArray < int > p(nRows()); 1028 removeRows(nums, n, p.get_ptr()); 1029 return; 1030 } 1031 1032 for(int i = nRows() - 1; i >= 0; --i) 1033 perm[i] = i; 1034 1035 while(n--) 1036 perm[nums[n]] = -1; 1037 1038 removeRows(perm); 1039 } 1040 1041 /// Removes rows from \p start to \p end (including both). 1042 virtual void removeRowRange(int start, int end, int perm[] = 0) 1043 { 1044 1045 if(perm == 0) 1046 { 1047 int i = end - start + 1; 1048 DataArray < int > p(i); 1049 1050 while(--i >= 0) 1051 p[i] = start + i; 1052 1053 removeRows(p.get_ptr(), end - start + 1); 1054 return; 1055 } 1056 1057 int i; 1058 1059 for(i = 0; i < start; ++i) 1060 perm[i] = i; 1061 1062 for(; i <= end; ++i) 1063 perm[i] = -1; 1064 1065 for(; i < nRows(); ++i) 1066 perm[i] = i; 1067 1068 removeRows(perm); 1069 } 1070 1071 /// Removes \p i 'th column. 1072 virtual void removeCol(int i) 1073 { 1074 if(i < 0) 1075 return; 1076 1077 doRemoveCol(i); 1078 } 1079 1080 /// Removes column with identifier \p id. 1081 virtual void removeCol(SPxColId id) 1082 { 1083 removeCol(number(id)); 1084 } 1085 1086 /// Removes multiple columns. 1087 /** This method removes all LPColBase%s from the SPxLPBase with an index \p i such that \p perm[i] < 0. Upon 1088 * completion, \p perm[i] >= 0 indicates the new index where the \p i 'th LPColBase has been moved to due to this 1089 * removal. Note, that \p perm must point to an array of at least #nCols() ints. 1090 */ 1091 virtual void removeCols(int perm[]) 1092 { 1093 doRemoveCols(perm); 1094 } 1095 1096 /// 1097 virtual void removeCols(SPxColId id[], int n, int perm[] = 0) 1098 { 1099 1100 if(perm == 0) 1101 { 1102 DataArray < int > p(nCols()); 1103 removeCols(id, n, p.get_ptr()); 1104 return; 1105 } 1106 1107 for(int i = nCols() - 1; i >= 0; --i) 1108 perm[i] = i; 1109 1110 while(n--) 1111 perm[number(id[n])] = -1; 1112 1113 removeCols(perm); 1114 } 1115 1116 /// Removes \p n LPCols. 1117 /** Removing multiple columns with one method invocation is available in two flavours. An array \p perm can be passed 1118 * as third argument or not. If given, \p perm must be an array at least of size #nCols(). It is used to return the 1119 * permutations resulting from this removal: \p perm[i] < 0 indicates, that the element to index \p i has been 1120 * removed. Otherwise, \p perm[i] is the new index of the element with index \p i before the removal. 1121 */ 1122 virtual void removeCols(int nums[], int n, int perm[] = 0) 1123 { 1124 1125 if(perm == 0) 1126 { 1127 DataArray < int > p(nCols()); 1128 removeCols(nums, n, p.get_ptr()); 1129 return; 1130 } 1131 1132 for(int i = nCols() - 1; i >= 0; --i) 1133 perm[i] = i; 1134 1135 while(n--) 1136 perm[nums[n]] = -1; 1137 1138 removeCols(perm); 1139 } 1140 1141 /// Removes columns from \p start to \p end (including both). 1142 virtual void removeColRange(int start, int end, int perm[] = 0) 1143 { 1144 1145 if(perm == 0) 1146 { 1147 int i = end - start + 1; 1148 DataArray < int > p(i); 1149 1150 while(--i >= 0) 1151 p[i] = start + i; 1152 1153 removeCols(p.get_ptr(), end - start + 1); 1154 return; 1155 } 1156 1157 int i; 1158 1159 for(i = 0; i < start; ++i) 1160 perm[i] = i; 1161 1162 for(; i <= end; ++i) 1163 perm[i] = -1; 1164 1165 for(; i < nCols(); ++i) 1166 perm[i] = i; 1167 1168 removeCols(perm); 1169 } 1170 1171 /// clears the LP. 1172 virtual void clear() 1173 { 1174 1175 LPRowSetBase<R>::clear(); 1176 LPColSetBase<R>::clear(); 1177 thesense = MAXIMIZE; 1178 offset = 0; 1179 _isScaled = false; 1180 lp_scaler = nullptr; 1181 LPColSetBase<R>::scaleExp.clear(); 1182 LPRowSetBase<R>::scaleExp.clear(); 1183 } 1184 1185 ///@} 1186 1187 // ------------------------------------------------------------------------------------------------------------------ 1188 /**@name IO */ 1189 ///@{ 1190 1191 /// Reads LP in LP format from input stream \p in. 1192 virtual bool readLPF(std::istream& in, NameSet* rowNames = 0, NameSet* colNames = 0, 1193 DIdxSet* intVars = 0); 1194 1195 /// Reads an LP in MPS format from input stream \p in. 1196 virtual bool readMPS(std::istream& in, NameSet* rowNames = 0, NameSet* colNames = 0, 1197 DIdxSet* intVars = 0); 1198 1199 /// Reads LP in LP or MPS format from input stream \p in. 1200 /**@param in input stream. 1201 * @param rowNames contains after the call the names of the constraints (rows) in the same order as the rows in the 1202 * LP. Constraints without a name (only possible with LPF files) are automatically assigned a name. 1203 * Maybe 0 if the names are not needed. 1204 * @param colNames contains after the call the names of the variables (columns) in the same order as the columns in 1205 * the LP. Maybe 0 if the names are not needed. 1206 * @param intVars contains after the call the indices of those variables that where marked as beeing integer in the 1207 * file. Maybe 0 if the information is not needed. 1208 * @todo Make sure the Id's in the NameSet%s are the same as in the LP. 1209 */ 1210 virtual bool read(std::istream& in, NameSet* rowNames = 0, NameSet* colNames = 0, 1211 DIdxSet* intVars = 0) 1212 { 1213 bool ok; 1214 char c; 1215 1216 in.get(c); 1217 in.putback(c); 1218 1219 /* MPS starts either with a comment mark '*' or with the keyword 'NAME' at the first column. LPF starts either 1220 * with blanks, a comment mark '\' or with the keyword "MAX" or "MIN" in upper or lower case. There is no 1221 * possible valid LPF file starting with a '*' or 'N'. 1222 */ 1223 ok = ((c == '*') || (c == 'N')) 1224 ? readMPS(in, rowNames, colNames, intVars) 1225 : readLPF(in, rowNames, colNames, intVars); 1226 1227 return ok; 1228 } 1229 1230 /// Reads LP from a file. 1231 virtual bool readFile(const char* filename, NameSet* rowNames = 0, NameSet* colNames = 0, 1232 DIdxSet* intVars = 0) 1233 { 1234 1235 spxifstream file(filename); 1236 1237 if(!file) 1238 return false; 1239 1240 return read(file, rowNames, colNames, intVars); 1241 } 1242 1243 /** Writes a file in LP format to \p out. If \p rowNames and \p colNames are \c NULL, default names are used for the 1244 * constraints and variables. If \p intVars is not \c NULL, the variables contained in it are marked as integer in 1245 * the output. 1246 */ 1247 virtual void writeLPF(std::ostream& out, const NameSet* rowNames, const NameSet* colNames, 1248 const DIdxSet* p_intvars = 0) const; 1249 1250 /// Writes a file in MPS format to \p out. 1251 virtual void writeMPS(std::ostream& out, const NameSet* rowNames, const NameSet* colNames, 1252 const DIdxSet* p_intvars = 0) const; 1253 1254 /// Write loaded LP to \p filename. 1255 virtual void writeFileLPBase(const char* filename, const NameSet* rowNames = 0, 1256 const NameSet* colNames = 0, const DIdxSet* p_intvars = 0) const 1257 { 1258 1259 std::ofstream tmp(filename); 1260 size_t len_f = strlen(filename); 1261 1262 if(len_f > 4 && filename[len_f - 1] == 's' && filename[len_f - 2] == 'p' 1263 && filename[len_f - 3] == 'm' && filename[len_f - 4] == '.') 1264 { 1265 writeMPS(tmp, rowNames, colNames, p_intvars); 1266 } 1267 else 1268 { 1269 writeLPF(tmp, rowNames, colNames, p_intvars); 1270 } 1271 } 1272 1273 /** prints problem statistics */ 1274 void printProblemStatistics(std::ostream& os) 1275 { 1276 int countLower = 0; 1277 int countUpper = 0; 1278 int countBoxed = 0; 1279 int countFreeCol = 0; 1280 1281 int countEqual = 0; 1282 int countLhs = 0; 1283 int countRhs = 0; 1284 int countRanged = 0; 1285 int countFreeRow = 0; 1286 1287 for(int i = 0; i < nCols(); i++) 1288 { 1289 bool hasLower = false; 1290 bool hasUpper = false; 1291 1292 if(lower(i) > R(-infinity)) 1293 { 1294 countLower++; 1295 hasLower = true; 1296 } 1297 1298 if(upper(i) < R(infinity)) 1299 { 1300 countUpper++; 1301 hasUpper = true; 1302 } 1303 1304 if(hasUpper && hasLower) 1305 { 1306 countBoxed++; 1307 countLower--; 1308 countUpper--; 1309 } 1310 1311 if(!hasUpper && !hasLower) 1312 countFreeCol++; 1313 } 1314 1315 for(int i = 0; i < nRows(); i++) 1316 { 1317 bool hasRhs = false; 1318 bool hasLhs = false; 1319 1320 if(lhs(i) > R(-infinity)) 1321 { 1322 countLhs++; 1323 hasLhs = true; 1324 } 1325 1326 if(rhs(i) < R(infinity)) 1327 { 1328 countRhs++; 1329 hasRhs = true; 1330 } 1331 1332 if(hasRhs && hasLhs) 1333 { 1334 if(EQ(lhs(i), rhs(i), this->tolerances()->epsilon())) 1335 countEqual++; 1336 else 1337 countRanged++; 1338 1339 countLhs--; 1340 countRhs--; 1341 } 1342 1343 if(!hasRhs && !hasLhs) 1344 countFreeRow++; 1345 } 1346 1347 SPxOut::setFixed(os); 1348 os << " Columns : " << nCols() << "\n" 1349 << " boxed : " << countBoxed << "\n" 1350 << " lower bound : " << countLower << "\n" 1351 << " upper bound : " << countUpper << "\n" 1352 << " free : " << countFreeCol << "\n" 1353 << " Rows : " << nRows() << "\n" 1354 << " equal : " << countEqual << "\n" 1355 << " ranged : " << countRanged << "\n" 1356 << " lhs : " << countLhs << "\n" 1357 << " rhs : " << countRhs << "\n" 1358 << " free : " << countFreeRow << "\n" 1359 << " Nonzeros : " << nNzos() << "\n" 1360 << " per column : " << R(nNzos()) / R(nCols()) << "\n" 1361 << " per row : " << R(nNzos()) / R(nRows()) << "\n" 1362 << " sparsity : " << R(nNzos()) / R(nCols()) / R(nRows()) << "\n" 1363 << " min. abs. value : " << R(minAbsNzo()) << "\n" 1364 << " max. abs. value : " << R(maxAbsNzo()) << "\n"; 1365 } 1366 1367 ///@} 1368 1369 // ------------------------------------------------------------------------------------------------------------------ 1370 /**@name Manipulation */ 1371 ///@{ 1372 1373 /// Changes objective vector to \p newObj. \p scale determines whether the new data should be scaled 1374 virtual void changeObj(const VectorBase<R>& newObj, bool scale = false) 1375 { 1376 changeMaxObj(newObj, scale); 1377 1378 if(spxSense() == MINIMIZE) 1379 LPColSetBase<R>::maxObj_w() *= -1; 1380 } 1381 1382 /// changes \p i 'th objective vector element to \p newVal. \p scale determines whether the new data should be scaled 1383 virtual void changeObj(int i, const R& newVal, bool scale = false) 1384 { 1385 changeMaxObj(i, newVal, scale); 1386 1387 if(spxSense() == MINIMIZE) 1388 LPColSetBase<R>::maxObj_w(i) *= -1; 1389 } 1390 1391 /// changes \p i 'th objective vector element to \p newVal. 1392 template < class S > 1393 void changeObj(int i, const S* newVal) 1394 { 1395 LPColSetBase<R>::maxObj_w(i) = *newVal; 1396 1397 if(spxSense() == MINIMIZE) 1398 LPColSetBase<R>::maxObj_w(i) *= -1; 1399 1400 assert(isConsistent()); 1401 } 1402 1403 /// Changes objective value of column with identifier \p id to \p newVal. \p scale determines whether the new data should be scaled 1404 virtual void changeObj(SPxColId id, const R& newVal, bool scale = false) 1405 { 1406 this->changeObj(number(id), newVal, scale); 1407 } 1408 1409 /// Changes objective vector to \p newObj. \p scale determines whether the new data should be scaled 1410 virtual void changeMaxObj(const VectorBase<R>& newObj, bool scale = false) 1411 { 1412 assert(maxObj().dim() == newObj.dim()); 1413 1414 if(scale) 1415 { 1416 assert(_isScaled); 1417 assert(lp_scaler); 1418 1419 for(int i = 0; i < maxObj().dim(); i++) 1420 LPColSetBase<R>::maxObj_w(i) = lp_scaler->scaleObj(*this, i, newObj[i]); 1421 } 1422 else 1423 LPColSetBase<R>::maxObj_w() = newObj; 1424 1425 assert(isConsistent()); 1426 } 1427 1428 /// changes \p i 'th objective vector element to \p newVal. \p scale determines whether the new data should be scaled 1429 virtual void changeMaxObj(int i, const R& newVal, bool scale = false) 1430 { 1431 if(scale) 1432 { 1433 assert(_isScaled); 1434 assert(lp_scaler); 1435 LPColSetBase<R>::maxObj_w(i) = lp_scaler->scaleObj(*this, i, newVal); 1436 } 1437 else 1438 LPColSetBase<R>::maxObj_w(i) = newVal; 1439 1440 assert(isConsistent()); 1441 } 1442 1443 /// changes \p i 'th objective vector element to \p newVal. 1444 template < class S > 1445 void changeMaxObj(int i, const S* newVal) 1446 { 1447 LPColSetBase<R>::maxObj_w(i) = *newVal; 1448 assert(isConsistent()); 1449 } 1450 1451 /// Changes objective value of column with identifier \p id to \p newVal. \p scale determines whether the new data should be scaled 1452 virtual void changeMaxObj(SPxColId id, const R& newVal, bool scale = false) 1453 { 1454 changeMaxObj(number(id), newVal, scale); 1455 } 1456 1457 /// Changes vector of lower bounds to \p newLower. \p scale determines whether the new data should be scaled 1458 virtual void changeLower(const VectorBase<R>& newLower, bool scale = false) 1459 { 1460 assert(lower().dim() == newLower.dim()); 1461 1462 if(scale) 1463 { 1464 assert(_isScaled); 1465 assert(lp_scaler); 1466 1467 for(int i = 0; i < lower().dim(); i++) 1468 LPColSetBase<R>::lower_w(i) = lp_scaler->scaleLower(*this, i, newLower[i]); 1469 } 1470 else 1471 LPColSetBase<R>::lower_w() = newLower; 1472 1473 assert(isConsistent()); 1474 } 1475 1476 /// changes \p i 'th lower bound to \p newLower. \p scale determines whether the new data should be scaled 1477 virtual void changeLower(int i, const R& newLower, bool scale = false) 1478 { 1479 if(scale && newLower > R(-infinity)) 1480 { 1481 assert(_isScaled); 1482 assert(lp_scaler); 1483 LPColSetBase<R>::lower_w(i) = lp_scaler->scaleLower(*this, i, newLower); 1484 } 1485 else 1486 LPColSetBase<R>::lower_w(i) = newLower; 1487 1488 assert(isConsistent()); 1489 } 1490 1491 /// changes \p i 'th lower bound to \p newLower. 1492 template < class S > 1493 void changeLower(int i, const S* newLower) 1494 { 1495 LPColSetBase<R>::lower_w(i) = *newLower; 1496 assert(isConsistent()); 1497 } 1498 1499 /// changes lower bound of column with identifier \p id to \p newLower. \p scale determines whether the new data should be scaled 1500 virtual void changeLower(SPxColId id, const R& newLower, bool scale = false) 1501 { 1502 changeLower(number(id), newLower, scale); 1503 } 1504 1505 /// Changes vector of upper bounds to \p newUpper. \p scale determines whether the new data should be scaled 1506 virtual void changeUpper(const VectorBase<R>& newUpper, bool scale = false) 1507 { 1508 assert(upper().dim() == newUpper.dim()); 1509 1510 if(scale) 1511 { 1512 assert(_isScaled); 1513 assert(lp_scaler); 1514 1515 for(int i = 0; i < upper().dim(); i++) 1516 LPColSetBase<R>::upper_w(i) = lp_scaler->scaleUpper(*this, i, newUpper[i]); 1517 } 1518 else 1519 LPColSetBase<R>::upper_w() = newUpper; 1520 1521 assert(isConsistent()); 1522 } 1523 1524 /// Changes \p i 'th upper bound to \p newUpper. \p scale determines whether the new data should be scaled 1525 virtual void changeUpper(int i, const R& newUpper, bool scale = false) 1526 { 1527 if(scale && newUpper < R(infinity)) 1528 { 1529 assert(_isScaled); 1530 assert(lp_scaler); 1531 LPColSetBase<R>::upper_w(i) = lp_scaler->scaleUpper(*this, i, newUpper); 1532 } 1533 else 1534 LPColSetBase<R>::upper_w(i) = newUpper; 1535 1536 assert(isConsistent()); 1537 } 1538 1539 /// Changes \p i 'th upper bound to \p newUpper. 1540 template < class S > 1541 void changeUpper(int i, const S* newUpper) 1542 { 1543 LPColSetBase<R>::upper_w(i) = *newUpper; 1544 assert(isConsistent()); 1545 } 1546 1547 /// Changes upper bound of column with identifier \p id to \p newLower. \p scale determines whether the new data should be scaled 1548 virtual void changeUpper(SPxColId id, const R& newUpper, bool scale = false) 1549 { 1550 changeUpper(number(id), newUpper, scale); 1551 } 1552 1553 /// Changes variable bounds to \p newLower and \p newUpper. \p scale determines whether the new data should be scaled 1554 virtual void changeBounds(const VectorBase<R>& newLower, const VectorBase<R>& newUpper, 1555 bool scale = false) 1556 { 1557 changeLower(newLower, scale); 1558 changeUpper(newUpper, scale); 1559 assert(isConsistent()); 1560 } 1561 1562 /// Changes bounds of column \p i to \p newLower and \p newUpper. \p scale determines whether the new data should be scaled 1563 virtual void changeBounds(int i, const R& newLower, const R& newUpper, bool scale = false) 1564 { 1565 changeLower(i, newLower, scale); 1566 changeUpper(i, newUpper, scale); 1567 assert(isConsistent()); 1568 } 1569 1570 /// Changes bounds of column \p i to \p newLower and \p newUpper. 1571 template < class S > 1572 void changeBounds(int i, const S* newLower, const S* newUpper) 1573 { 1574 LPColSetBase<R>::lower_w(i) = *newLower; 1575 LPColSetBase<R>::upper_w(i) = *newUpper; 1576 assert(isConsistent()); 1577 } 1578 1579 /// Changes bounds of column with identifier \p id. \p scale determines whether the new data should be scaled 1580 virtual void changeBounds(SPxColId id, const R& newLower, const R& newUpper, bool scale = false) 1581 { 1582 changeBounds(number(id), newLower, newUpper, scale); 1583 } 1584 1585 /// Changes left hand side vector for constraints to \p newLhs. \p scale determines whether the new data should be scaled 1586 virtual void changeLhs(const VectorBase<R>& newLhs, bool scale = false) 1587 { 1588 assert(lhs().dim() == newLhs.dim()); 1589 1590 if(scale) 1591 { 1592 assert(_isScaled); 1593 assert(lp_scaler); 1594 1595 for(int i = 0; i < lhs().dim(); i++) 1596 LPRowSetBase<R>::lhs_w(i) = lp_scaler->scaleLhs(*this, i, newLhs[i]); 1597 } 1598 else 1599 LPRowSetBase<R>::lhs_w() = newLhs; 1600 1601 assert(isConsistent()); 1602 } 1603 1604 /// Changes \p i 'th left hand side value to \p newLhs. \p scale determines whether the new data should be scaled 1605 virtual void changeLhs(int i, const R& newLhs, bool scale = false) 1606 { 1607 if(scale && newLhs > R(-infinity)) 1608 { 1609 assert(_isScaled); 1610 assert(lp_scaler); 1611 LPRowSetBase<R>::lhs_w(i) = lp_scaler->scaleLhs(*this, i, newLhs); 1612 } 1613 else 1614 LPRowSetBase<R>::lhs_w(i) = newLhs; 1615 1616 assert(isConsistent()); 1617 } 1618 1619 /// Changes \p i 'th left hand side value to \p newLhs. 1620 template < class S > 1621 void changeLhs(int i, const S* newLhs) 1622 { 1623 LPRowSetBase<R>::lhs_w(i) = *newLhs; 1624 assert(isConsistent()); 1625 } 1626 1627 /// Changes left hand side value for row with identifier \p id. \p scale determines whether the new data should be scaled 1628 virtual void changeLhs(SPxRowId id, const R& newLhs, bool scale = false) 1629 { 1630 changeLhs(number(id), newLhs, scale); 1631 } 1632 1633 /// Changes right hand side vector for constraints to \p newRhs. \p scale determines whether the new data should be scaled 1634 virtual void changeRhs(const VectorBase<R>& newRhs, bool scale = false) 1635 { 1636 assert(rhs().dim() == newRhs.dim()); 1637 1638 if(scale) 1639 { 1640 assert(_isScaled); 1641 assert(lp_scaler); 1642 1643 for(int i = 0; i < rhs().dim(); i++) 1644 LPRowSetBase<R>::rhs_w(i) = lp_scaler->scaleRhs(*this, i, newRhs[i]); 1645 } 1646 else 1647 LPRowSetBase<R>::rhs_w() = newRhs; 1648 1649 assert(isConsistent()); 1650 } 1651 1652 /// Changes \p i 'th right hand side value to \p newRhs. \p scale determines whether the new data should be scaled 1653 virtual void changeRhs(int i, const R& newRhs, bool scale = false) 1654 { 1655 if(scale && newRhs < R(infinity)) 1656 { 1657 assert(_isScaled); 1658 assert(lp_scaler); 1659 LPRowSetBase<R>::rhs_w(i) = lp_scaler->scaleRhs(*this, i, newRhs); 1660 } 1661 else 1662 LPRowSetBase<R>::rhs_w(i) = newRhs; 1663 1664 assert(isConsistent()); 1665 } 1666 1667 /// Changes right hand side value for row with identifier \p id. \p scale determines whether the new data should be scaled 1668 virtual void changeRhs(SPxRowId id, const R& newRhs, bool scale = false) 1669 { 1670 changeRhs(number(id), newRhs, scale); 1671 } 1672 1673 /// Changes left and right hand side vectors. \p scale determines whether the new data should be scaled 1674 virtual void changeRange(const VectorBase<R>& newLhs, const VectorBase<R>& newRhs, 1675 bool scale = false) 1676 { 1677 changeLhs(newLhs, scale); 1678 changeRhs(newRhs, scale); 1679 assert(isConsistent()); 1680 } 1681 1682 /// Changes left and right hand side of row \p i. \p scale determines whether the new data should be scaled 1683 virtual void changeRange(int i, const R& newLhs, const R& newRhs, bool scale = false) 1684 { 1685 changeLhs(i, newLhs, scale); 1686 changeRhs(i, newRhs, scale); 1687 assert(isConsistent()); 1688 } 1689 1690 /// Changes left and right hand side of row \p i. 1691 template < class S > 1692 void changeRange(int i, const S* newLhs, const S* newRhs) 1693 { 1694 LPRowSetBase<R>::lhs_w(i) = *newLhs; 1695 LPRowSetBase<R>::rhs_w(i) = *newRhs; 1696 assert(isConsistent()); 1697 } 1698 1699 /// Changes left and right hand side of row with identifier \p id. \p scale determines whether the new data should be scaled 1700 virtual void changeRange(SPxRowId id, const R& newLhs, const R& newRhs, bool scale = false) 1701 { 1702 changeRange(number(id), newLhs, newRhs, scale); 1703 } 1704 1705 /// Changes row objective function vector to \p newRowObj. \p scale determines whether the new data should be scaled 1706 virtual void changeRowObj(const VectorBase<R>& newRowObj, bool scale = false) 1707 { 1708 assert(maxRowObj().dim() == newRowObj.dim()); 1709 LPRowSetBase<R>::obj_w() = newRowObj; 1710 1711 if(spxSense() == MINIMIZE) 1712 LPRowSetBase<R>::obj_w() *= -1; 1713 1714 assert(isConsistent()); 1715 } 1716 1717 /// Changes \p i 'th row objective function value to \p newRowObj. \p scale determines whether the new data should be scaled 1718 virtual void changeRowObj(int i, const R& newRowObj, bool scale = false) 1719 { 1720 LPRowSetBase<R>::obj_w(i) = newRowObj; 1721 1722 if(spxSense() == MINIMIZE) 1723 LPRowSetBase<R>::obj_w(i) *= -1; 1724 1725 assert(isConsistent()); 1726 } 1727 1728 /// Changes row objective function value for row with identifier \p id. \p scale determines whether the new data should be scaled 1729 virtual void changeRowObj(SPxRowId id, const R& newRowObj, bool scale = false) 1730 { 1731 changeRowObj(number(id), newRowObj, scale); 1732 } 1733 1734 /// Clears row objective function values for all rows 1735 virtual void clearRowObjs() 1736 { 1737 LPRowSetBase<R>::obj_w().clear(); 1738 } 1739 1740 /// Replaces \p i 'th row of LP with \p newRow. \p scale determines whether the new data should be scaled 1741 virtual void changeRow(int n, const LPRowBase<R>& newRow, bool scale = false) 1742 { 1743 if(n < 0) 1744 return; 1745 1746 int j; 1747 SVectorBase<R>& row = rowVector_w(n); 1748 1749 for(j = row.size() - 1; j >= 0; --j) 1750 { 1751 SVectorBase<R>& col = colVector_w(row.index(j)); 1752 int position = col.pos(n); 1753 1754 assert(position != -1); 1755 1756 if(position >= 0) 1757 col.remove(position); 1758 } 1759 1760 row.clear(); 1761 1762 changeLhs(n, newRow.lhs(), scale); 1763 changeRhs(n, newRow.rhs(), scale); 1764 changeRowObj(n, newRow.obj(), scale); 1765 1766 const SVectorBase<R>& newrow = newRow.rowVector(); 1767 1768 for(j = newrow.size() - 1; j >= 0; --j) 1769 { 1770 int idx = newrow.index(j); 1771 R val = newrow.value(j); 1772 1773 if(scale) 1774 val = spxLdexp(val, LPRowSetBase<R>::scaleExp[n] + LPColSetBase<R>::scaleExp[idx]); 1775 1776 LPRowSetBase<R>::add2(n, 1, &idx, &val); 1777 LPColSetBase<R>::add2(idx, 1, &n, &val); 1778 } 1779 1780 assert(isConsistent()); 1781 } 1782 1783 /// Replaces row with identifier \p id with \p newRow. \p scale determines whether the new data should be scaled 1784 virtual void changeRow(SPxRowId id, const LPRowBase<R>& newRow, bool scale = false) 1785 { 1786 changeRow(number(id), newRow, scale); 1787 } 1788 1789 /// Replaces \p i 'th column of LP with \p newCol. \p scale determines whether the new data should be scaled 1790 virtual void changeCol(int n, const LPColBase<R>& newCol, bool scale = false) 1791 { 1792 if(n < 0) 1793 return; 1794 1795 int j; 1796 SVectorBase<R>& col = colVector_w(n); 1797 1798 for(j = col.size() - 1; j >= 0; --j) 1799 { 1800 SVectorBase<R>& row = rowVector_w(col.index(j)); 1801 int position = row.pos(n); 1802 1803 assert(position != -1); 1804 1805 if(position >= 0) 1806 row.remove(position); 1807 } 1808 1809 col.clear(); 1810 1811 changeUpper(n, newCol.upper(), scale); 1812 changeLower(n, newCol.lower(), scale); 1813 changeObj(n, newCol.obj(), scale); 1814 1815 const SVectorBase<R>& newcol = newCol.colVector(); 1816 1817 for(j = newcol.size() - 1; j >= 0; --j) 1818 { 1819 int idx = newcol.index(j); 1820 R val = newcol.value(j); 1821 1822 if(scale) 1823 val = spxLdexp(val, LPColSetBase<R>::scaleExp[n] + LPRowSetBase<R>::scaleExp[idx]); 1824 1825 LPColSetBase<R>::add2(n, 1, &idx, &val); 1826 LPRowSetBase<R>::add2(idx, 1, &n, &val); 1827 } 1828 1829 assert(isConsistent()); 1830 } 1831 1832 /// Replaces column with identifier \p id with \p newCol. \p scale determines whether the new data should be scaled 1833 virtual void changeCol(SPxColId id, const LPColBase<R>& newCol, bool scale = false) 1834 { 1835 changeCol(number(id), newCol, scale); 1836 } 1837 1838 /// Changes LP element (\p i, \p j) to \p val. \p scale determines whether the new data should be scaled 1839 virtual void changeElement(int i, int j, const R& val, bool scale = false) 1840 { 1841 if(i < 0 || j < 0) 1842 return; 1843 1844 SVectorBase<R>& row = rowVector_w(i); 1845 SVectorBase<R>& col = colVector_w(j); 1846 1847 if(isNotZero(val, this->tolerances()->epsilon())) 1848 { 1849 R newVal; 1850 1851 if(scale) 1852 { 1853 assert(_isScaled); 1854 assert(lp_scaler); 1855 newVal = lp_scaler->scaleElement(*this, i, j, val); 1856 } 1857 else 1858 newVal = val; 1859 1860 if(row.pos(j) >= 0 && col.pos(i) >= 0) 1861 { 1862 row.value(row.pos(j)) = newVal; 1863 col.value(col.pos(i)) = newVal; 1864 } 1865 else 1866 { 1867 LPRowSetBase<R>::add2(i, 1, &j, &newVal); 1868 LPColSetBase<R>::add2(j, 1, &i, &newVal); 1869 } 1870 } 1871 else if(row.pos(j) >= 0 && col.pos(i) >= 0) 1872 { 1873 row.remove(row.pos(j)); 1874 col.remove(col.pos(i)); 1875 } 1876 1877 assert(isConsistent()); 1878 } 1879 1880 /// Changes LP element (\p i, \p j) to \p val. 1881 template < class S > 1882 void changeElement(int i, int j, const S* val) 1883 { 1884 if(i < 0 || j < 0) 1885 return; 1886 1887 SVectorBase<R>& row = rowVector_w(i); 1888 SVectorBase<R>& col = colVector_w(j); 1889 1890 if(mpq_get_d(*val) != R(0)) 1891 { 1892 if(row.pos(j) >= 0 && col.pos(i) >= 0) 1893 { 1894 row.value(row.pos(j)) = *val; 1895 col.value(col.pos(i)) = *val; 1896 } 1897 else 1898 { 1899 LPRowSetBase<R>::add2(i, 1, &j, val); 1900 LPColSetBase<R>::add2(j, 1, &i, val); 1901 } 1902 } 1903 else if(row.pos(j) >= 0 && col.pos(i) >= 0) 1904 { 1905 row.remove(row.pos(j)); 1906 col.remove(col.pos(i)); 1907 } 1908 1909 assert(isConsistent()); 1910 } 1911 1912 /// Changes LP element identified by (\p rid, \p cid) to \p val. \p scale determines whether the new data should be scaled 1913 virtual void changeElement(SPxRowId rid, SPxColId cid, const R& val, bool scale = false) 1914 { 1915 changeElement(number(rid), number(cid), val, scale); 1916 } 1917 1918 /// Changes optimization sense to \p sns. 1919 virtual void changeSense(SPxSense sns) 1920 { 1921 if(sns != thesense) 1922 { 1923 LPColSetBase<R>::maxObj_w() *= -1; 1924 LPRowSetBase<R>::obj_w() *= -1; 1925 } 1926 1927 thesense = sns; 1928 } 1929 1930 template <typename T> 1931 void changeObjOffset(const T& o) 1932 { 1933 offset = o; // Converts o into type R. Example Rational into 1934 // R 1935 } 1936 1937 /// Computes activity of the rows for a given primal vector; activity does not need to be zero 1938 /// @throw SPxInternalCodeException if the dimension of primal vector does not match number of columns or if the 1939 /// dimension of the activity vector does not match the number of rows 1940 /// \p unscaled determines whether the returned data should be unscaled (if scaling was applied prior) 1941 virtual void computePrimalActivity(const VectorBase<R>& primal, VectorBase<R>& activity, 1942 const bool unscaled = true) const; 1943 1944 /// Updates activity of the rows for a given primal vector; activity does not need to be zero 1945 /// @throw SPxInternalCodeException if the dimension of primal vector does not match number of columns or if the 1946 /// dimension of the activity vector does not match the number of rows 1947 virtual void addPrimalActivity(const SVectorBase<R>& primal, VectorBase<R>& activity) const 1948 { 1949 if(activity.dim() != nRows()) 1950 { 1951 throw SPxInternalCodeException("XSPXLP03 Activity vector computing row activity has wrong dimension"); 1952 } 1953 1954 for(int i = primal.size() - 1; i >= 0; i--) 1955 { 1956 assert(primal.index(i) >= 0); 1957 assert(primal.index(i) < nCols()); 1958 activity.multAdd(primal.value(i), colVector(primal.index(i))); 1959 } 1960 } 1961 1962 /// Computes "dual" activity of the columns for a given dual vector, i.e., y^T A; activity does not need to be zero 1963 /// @throw SPxInternalCodeException if dimension of dual vector does not match number of rows or if the dimension of 1964 /// the activity vector does not match the number of columns 1965 virtual void computeDualActivity(const VectorBase<R>& dual, VectorBase<R>& activity, 1966 const bool unscaled = true) const; 1967 1968 /// Updates "dual" activity of the columns for a given dual vector, i.e., y^T A; activity does not need to be zero 1969 /// @throw SPxInternalCodeException if dimension of dual vector does not match number of rows or if the dimension of 1970 /// the activity vector does not match the number of columns 1971 virtual void addDualActivity(const SVectorBase<R>& dual, VectorBase<R>& activity) const 1972 { 1973 if(activity.dim() != nCols()) 1974 { 1975 throw SPxInternalCodeException("XSPXLP04 Activity vector computing dual activity has wrong dimension"); 1976 } 1977 1978 for(int i = dual.size() - 1; i >= 0; i--) 1979 { 1980 assert(dual.index(i) >= 0); 1981 assert(dual.index(i) < nRows()); 1982 activity.multAdd(dual.value(i), rowVector(dual.index(i))); 1983 } 1984 } 1985 1986 /// Updates "dual" activity of the columns for a given dual vector, i.e., y^T A; activity does not need to be zero 1987 /// @throw SPxInternalCodeException if dimension of dual vector does not match number of rows or if the dimension of 1988 /// the activity vector does not match the number of columns 1989 virtual void subDualActivity(const VectorBase<R>& dual, VectorBase<R>& activity) const 1990 { 1991 if(dual.dim() != nRows()) 1992 { 1993 throw SPxInternalCodeException("XSPXLP02 Dual vector for computing dual activity has wrong dimension"); 1994 } 1995 1996 if(activity.dim() != nCols()) 1997 { 1998 throw SPxInternalCodeException("XSPXLP04 Activity vector computing dual activity has wrong dimension"); 1999 } 2000 2001 for(int r = 0; r < nRows(); r++) 2002 { 2003 if(dual[r] != 0) 2004 activity.multSub(dual[r], rowVector(r)); 2005 } 2006 } 2007 2008 ///@} 2009 2010 // ------------------------------------------------------------------------------------------------------------------ 2011 /**@name Construction of dual problem */ 2012 ///@{ 2013 2014 /// Building the dual problem from a given LP 2015 /// @note primalRows must be as large as the number of unranged primal rows + 2 * the number of ranged primal rows. 2016 /// dualCols must have the identical size to the primal rows. 2017 virtual void buildDualProblem(SPxLPBase<R>& dualLP, SPxRowId primalRowIds[] = 0, 2018 SPxColId primalColIds[] = 0, 2019 SPxRowId dualRowIds[] = 0, SPxColId dualColIds[] = 0, int* nprimalrows = 0, int* nprimalcols = 0, 2020 int* ndualrows = 0, int* ndualcols = 0); 2021 2022 ///@} 2023 2024 // ------------------------------------------------------------------------------------------------------------------ 2025 /**@name Miscellaneous */ 2026 ///@{ 2027 2028 /// Consistency check. 2029 bool isConsistent() const 2030 { 2031 if(this->_tolerances == nullptr && nCols() != 0) 2032 return SPX_MSG_INCONSISTENT("SPxLPBase"); 2033 2034 #ifdef ENABLE_CONSISTENCY_CHECKS 2035 2036 for(int i = nCols() - 1; i >= 0; --i) 2037 { 2038 const SVectorBase<R>& v = colVector(i); 2039 2040 for(int j = v.size() - 1; j >= 0; --j) 2041 { 2042 const SVectorBase<R>& w = rowVector(v.index(j)); 2043 int n = w.pos(i); 2044 2045 if(n < 0) 2046 return SPX_MSG_INCONSISTENT("SPxLPBase"); 2047 2048 if(v.value(j) != w.value(n)) 2049 return SPX_MSG_INCONSISTENT("SPxLPBase"); 2050 } 2051 } 2052 2053 for(int i = nRows() - 1; i >= 0; --i) 2054 { 2055 const SVectorBase<R>& v = rowVector(i); 2056 2057 for(int j = v.size() - 1; j >= 0; --j) 2058 { 2059 const SVectorBase<R>& w = colVector(v.index(j)); 2060 int n = w.pos(i); 2061 2062 if(n < 0) 2063 return SPX_MSG_INCONSISTENT("SPxLPBase"); 2064 2065 if(v.value(j) != w.value(n)) 2066 return SPX_MSG_INCONSISTENT("SPxLPBase"); 2067 } 2068 } 2069 2070 return LPRowSetBase<R>::isConsistent() && LPColSetBase<R>::isConsistent(); 2071 #else 2072 return true; 2073 #endif 2074 } 2075 2076 ///@} 2077 2078 protected: 2079 2080 std::shared_ptr<Tolerances> _tolerances; 2081 2082 // ------------------------------------------------------------------------------------------------------------------ 2083 /**@name Protected write access */ 2084 ///@{ 2085 2086 /// Returns right hand side of row \p i. 2087 R& rhs_w(int i) 2088 { 2089 return LPRowSetBase<R>::rhs_w(i); 2090 } 2091 2092 /// Returns left hand side of row \p i. 2093 R& lhs_w(int i) 2094 { 2095 return LPRowSetBase<R>::lhs_w(i); 2096 } 2097 2098 /// Returns objective function value of row \p i. 2099 R& maxRowObj_w(int i) 2100 { 2101 return LPRowSetBase<R>::obj_w(i); 2102 } 2103 2104 /// Returns objective value of column \p i for maximization problem. 2105 R& maxObj_w(int i) 2106 { 2107 return LPColSetBase<R>::maxObj_w(i); 2108 } 2109 2110 /// Returns upper bound of column \p i. 2111 R& upper_w(int i) 2112 { 2113 return LPColSetBase<R>::upper_w(i); 2114 } 2115 2116 /// Returns lower bound of column \p i. 2117 R& lower_w(int i) 2118 { 2119 return LPColSetBase<R>::lower_w(i); 2120 } 2121 2122 ///@} 2123 2124 // ------------------------------------------------------------------------------------------------------------------ 2125 /**@name Protected helpers */ 2126 ///@{ 2127 2128 /// Returns the LP as an LPRowSetBase. 2129 const LPRowSetBase<R>* lprowset() const 2130 { 2131 return static_cast<const LPRowSetBase<R>*>(this); 2132 } 2133 2134 /// Returns the LP as an LPColSetBase. 2135 const LPColSetBase<R>* lpcolset() const 2136 { 2137 return static_cast<const LPColSetBase<R>*>(this); 2138 } 2139 2140 /// Internal helper method. 2141 virtual void doRemoveRow(int j) 2142 { 2143 2144 const SVectorBase<R>& vec = rowVector(j); 2145 2146 // remove row vector from column file 2147 for(int i = vec.size() - 1; i >= 0; --i) 2148 { 2149 SVectorBase<R>& remvec = colVector_w(vec.index(i)); 2150 int position = remvec.pos(j); 2151 2152 if(position >= 0) 2153 remvec.remove(position); 2154 } 2155 2156 // move last row to removed position 2157 int idx = nRows() - 1; 2158 2159 if(j != idx) 2160 { 2161 const SVectorBase<R>& l_vec = rowVector(idx); 2162 2163 for(int i = l_vec.size() - 1; i >= 0; --i) 2164 { 2165 SVectorBase<R>& movevec = colVector_w(l_vec.index(i)); 2166 int position = movevec.pos(idx); 2167 2168 assert(position != -1); 2169 2170 if(position >= 0) 2171 movevec.index(position) = j; 2172 } 2173 } 2174 2175 LPRowSetBase<R>::remove(j); 2176 } 2177 2178 /// Internal helper method. 2179 virtual void doRemoveRows(int perm[]) 2180 { 2181 int j = nCols(); 2182 2183 LPRowSetBase<R>::remove(perm); 2184 2185 for(int i = 0; i < j; ++i) 2186 { 2187 SVectorBase<R>& vec = colVector_w(i); 2188 2189 for(int k = vec.size() - 1; k >= 0; --k) 2190 { 2191 int idx = vec.index(k); 2192 2193 if(perm[idx] < 0) 2194 vec.remove(k); 2195 else 2196 vec.index(k) = perm[idx]; 2197 } 2198 } 2199 } 2200 2201 /// Internal helper method. 2202 virtual void doRemoveCol(int j) 2203 { 2204 2205 const SVectorBase<R>& vec = colVector(j); 2206 int i; 2207 2208 // remove column vector from row file 2209 for(i = vec.size() - 1; i >= 0; --i) 2210 { 2211 SVectorBase<R>& remvec = rowVector_w(vec.index(i)); 2212 int position = remvec.pos(j); 2213 2214 assert(position != -1); 2215 2216 if(position >= 0) 2217 remvec.remove(position); 2218 } 2219 2220 // move last column to removed position 2221 int idx = nCols() - 1; 2222 2223 if(j != idx) 2224 { 2225 const SVectorBase<R>& l_vec = colVector(idx); 2226 2227 for(i = l_vec.size() - 1; i >= 0; --i) 2228 { 2229 SVectorBase<R>& movevec = rowVector_w(l_vec.index(i)); 2230 int position = movevec.pos(idx); 2231 2232 assert(position != -1); 2233 2234 if(position >= 0) 2235 movevec.index(position) = j; 2236 } 2237 } 2238 2239 LPColSetBase<R>::remove(j); 2240 } 2241 2242 /// Internal helper method. 2243 virtual void doRemoveCols(int perm[]) 2244 { 2245 int nrows = nRows(); 2246 2247 LPColSetBase<R>::remove(perm); 2248 2249 for(int i = 0; i < nrows; ++i) 2250 { 2251 SVectorBase<R>& vec = rowVector_w(i); 2252 2253 for(int k = vec.size() - 1; k >= 0; --k) 2254 { 2255 int idx = vec.index(k); 2256 2257 if(perm[idx] < 0) 2258 vec.remove(k); 2259 else 2260 vec.index(k) = perm[idx]; 2261 } 2262 } 2263 } 2264 2265 /// Called after the last \p n rows have just been added. 2266 virtual void addedRows(int newrows) 2267 {} 2268 2269 /// Called after the last \p n columns have just been added. 2270 virtual void addedCols(int newcols) 2271 {} 2272 2273 /// 2274 void added2Set(SVSetBase<R>& set, const SVSetBase<R>& addset, int n) 2275 { 2276 2277 if(n == 0) 2278 return; 2279 2280 DataArray<int> moreArray(set.num()); 2281 int* more = moreArray.get_ptr(); 2282 2283 for(int i = set.num() - 1; i >= 0; --i) 2284 more[i] = 0; 2285 2286 int tot = 0; 2287 int end = addset.num(); 2288 2289 for(int i = addset.num() - n; i < end; ++i) 2290 { 2291 const SVectorBase<R>& vec = addset[i]; 2292 2293 tot += vec.size(); 2294 2295 for(int j = vec.size() - 1; j >= 0; --j) 2296 more[vec.index(j)]++; 2297 } 2298 2299 if(set.memMax() < tot) 2300 set.memRemax(tot); 2301 2302 for(int i = set.num() - 1; i >= 0; --i) 2303 { 2304 int j = set[i].size(); 2305 set.xtend(set[i], j + more[i]); 2306 set[i].set_size(j + more[i]); 2307 more[i] = j; 2308 } 2309 2310 for(int i = addset.num() - n; i < addset.num(); ++i) 2311 { 2312 const SVectorBase<R>& vec = addset[i]; 2313 2314 for(int j = vec.size() - 1; j >= 0; --j) 2315 { 2316 int k = vec.index(j); 2317 int m = more[k]++; 2318 SVectorBase<R>& l_xtend = set[k]; 2319 l_xtend.index(m) = i; 2320 l_xtend.value(m) = vec.value(j); 2321 } 2322 } 2323 } 2324 2325 ///@} 2326 2327 2328 private: 2329 2330 // ------------------------------------------------------------------------------------------------------------------ 2331 /**@name Private helpers */ 2332 ///@{ 2333 2334 /// Returns the LP as an LPRowBase<R>Set. 2335 SVectorBase<R>& colVector_w(int i) 2336 { 2337 return LPColSetBase<R>::colVector_w(i); 2338 } 2339 2340 /// 2341 SVectorBase<R>& rowVector_w(int i) 2342 { 2343 return LPRowSetBase<R>::rowVector_w(i); 2344 } 2345 2346 /// 2347 void doAddRow(const LPRowBase<R>& row, bool scale = false) 2348 { 2349 int idx = nRows(); 2350 int oldColNumber = nCols(); 2351 int newRowScaleExp = 0; 2352 2353 LPRowSetBase<R>::add(row); 2354 2355 SVectorBase<R>& vec = rowVector_w(idx); 2356 2357 DataArray <int>& colscaleExp = LPColSetBase<R>::scaleExp; 2358 2359 // compute new row scaling factor and apply it to the sides 2360 if(scale && lp_scaler) 2361 { 2362 newRowScaleExp = lp_scaler->computeScaleExp(vec, colscaleExp); 2363 2364 if(rhs(idx) < R(infinity)) 2365 rhs_w(idx) = spxLdexp(rhs_w(idx), newRowScaleExp); 2366 2367 if(lhs(idx) > R(-infinity)) 2368 lhs_w(idx) = spxLdexp(lhs_w(idx), newRowScaleExp); 2369 2370 maxRowObj_w(idx) = spxLdexp(maxRowObj_w(idx), newRowScaleExp); 2371 2372 LPRowSetBase<R>::scaleExp[idx] = newRowScaleExp; 2373 } 2374 2375 // now insert nonzeros to column file also 2376 for(int j = vec.size() - 1; j >= 0; --j) 2377 { 2378 int i = vec.index(j); 2379 2380 // apply new row and existing column scaling factors to new values in RowSet 2381 if(scale) 2382 vec.value(j) = spxLdexp(vec.value(j), newRowScaleExp + colscaleExp[i]); 2383 2384 R val = vec.value(j); 2385 2386 // create new columns if required 2387 if(i >= nCols()) 2388 { 2389 LPColBase<R> empty; 2390 2391 for(int k = nCols(); k <= i; ++k) 2392 LPColSetBase<R>::add(empty); 2393 } 2394 2395 assert(i < nCols()); 2396 LPColSetBase<R>::add2(i, 1, &idx, &val); 2397 } 2398 2399 addedRows(1); 2400 addedCols(nCols() - oldColNumber); 2401 } 2402 2403 /// 2404 void doAddRow(const R& lhsValue, const SVectorBase<R>& rowVec, const R& rhsValue, 2405 bool scale = false) 2406 { 2407 int idx = nRows(); 2408 int oldColNumber = nCols(); 2409 int newRowScaleExp = 0; 2410 2411 LPRowSetBase<R>::add(lhsValue, rowVec, rhsValue); 2412 2413 DataArray <int>& colscaleExp = LPColSetBase<R>::scaleExp; 2414 2415 // compute new row scaling factor and apply it to the sides 2416 if(scale) 2417 { 2418 newRowScaleExp = lp_scaler->computeScaleExp(rowVec, colscaleExp); 2419 2420 if(rhs(idx) < R(infinity)) 2421 rhs_w(idx) = spxLdexp(rhs_w(idx), newRowScaleExp); 2422 2423 if(lhs(idx) > R(-infinity)) 2424 lhs_w(idx) = spxLdexp(lhs_w(idx), newRowScaleExp); 2425 2426 maxRowObj_w(idx) = spxLdexp(maxRowObj_w(idx), newRowScaleExp); 2427 2428 LPRowSetBase<R>::scaleExp[idx] = newRowScaleExp; 2429 } 2430 2431 SVectorBase<R>& vec = rowVector_w(idx); 2432 2433 // now insert nonzeros to column file also 2434 for(int j = vec.size() - 1; j >= 0; --j) 2435 { 2436 int i = vec.index(j); 2437 2438 // apply new row and existing column scaling factors to new values in RowSet 2439 if(scale) 2440 vec.value(j) = spxLdexp(vec.value(j), newRowScaleExp + colscaleExp[i]); 2441 2442 R val = vec.value(j); 2443 2444 // create new columns if required 2445 if(i >= nCols()) 2446 { 2447 LPColBase<R> empty; 2448 2449 for(int k = nCols(); k <= i; ++k) 2450 LPColSetBase<R>::add(empty); 2451 } 2452 2453 assert(i < nCols()); 2454 LPColSetBase<R>::add2(i, 1, &idx, &val); 2455 } 2456 2457 addedRows(1); 2458 addedCols(nCols() - oldColNumber); 2459 } 2460 2461 /// 2462 void doAddRows(const LPRowSetBase<R>& set, bool scale = false) 2463 { 2464 int i, j, k, ii, idx; 2465 SVectorBase<R>* col; 2466 DataArray < int > newCols(nCols()); 2467 int oldRowNumber = nRows(); 2468 int oldColNumber = nCols(); 2469 2470 if(&set != this) 2471 LPRowSetBase<R>::add(set); 2472 2473 assert(LPRowSetBase<R>::isConsistent()); 2474 assert(LPColSetBase<R>::isConsistent()); 2475 2476 // count additional nonzeros per column 2477 for(i = nCols() - 1; i >= 0; --i) 2478 newCols[i] = 0; 2479 2480 for(i = set.num() - 1; i >= 0; --i) 2481 { 2482 const SVectorBase<R>& vec = set.rowVector(i); 2483 2484 for(j = vec.size() - 1; j >= 0; --j) 2485 { 2486 // create new columns if required 2487 ii = vec.index(j); 2488 2489 if(ii >= nCols()) 2490 { 2491 LPColBase<R> empty; 2492 newCols.reSize(ii + 1); 2493 2494 for(k = nCols(); k <= ii; ++k) 2495 { 2496 newCols[k] = 0; 2497 LPColSetBase<R>::add(empty); 2498 } 2499 } 2500 2501 assert(ii < nCols()); 2502 newCols[ii]++; 2503 } 2504 } 2505 2506 // extend columns as required (backward because of memory efficiency reasons) 2507 for(i = nCols() - 1; i >= 0; --i) 2508 { 2509 if(newCols[i] > 0) 2510 { 2511 int len = newCols[i] + colVector(i).size(); 2512 LPColSetBase<R>::xtend(i, len); 2513 2514 /* preset the sizes: beware that this can irritate a consistency check call from xtend(). We need to set the 2515 * sizes here, because a possible garbage collection called from xtend might destroy the sizes again. */ 2516 colVector_w(i).set_size(len); 2517 } 2518 } 2519 2520 // compute new row scaling factor and insert new elements to column file 2521 for(i = nRows() - 1; i >= oldRowNumber; --i) 2522 { 2523 SVectorBase<R>& vec = rowVector_w(i); 2524 int newRowScaleExp = 0; 2525 2526 DataArray <int>& colscaleExp = LPColSetBase<R>::scaleExp; 2527 2528 // compute new row scaling factor and apply it to the sides 2529 if(scale) 2530 { 2531 newRowScaleExp = lp_scaler->computeScaleExp(vec, colscaleExp); 2532 2533 if(rhs(i) < R(infinity)) 2534 rhs_w(i) = spxLdexp(rhs_w(i), newRowScaleExp); 2535 2536 if(lhs(i) > R(-infinity)) 2537 lhs_w(i) = spxLdexp(lhs_w(i), newRowScaleExp); 2538 2539 maxRowObj_w(i) = spxLdexp(maxRowObj_w(i), newRowScaleExp); 2540 2541 LPRowSetBase<R>::scaleExp[i] = newRowScaleExp; 2542 } 2543 2544 for(j = vec.size() - 1; j >= 0; --j) 2545 { 2546 k = vec.index(j); 2547 col = &colVector_w(k); 2548 idx = col->size() - newCols[k]; 2549 assert(newCols[k] > 0); 2550 assert(idx >= 0); 2551 newCols[k]--; 2552 col->index(idx) = i; 2553 2554 // apply new row and existing column scaling factors to both ColSet and RowSet 2555 if(scale) 2556 vec.value(j) = spxLdexp(vec.value(j), newRowScaleExp + colscaleExp[k]); 2557 2558 col->value(idx) = vec.value(j); 2559 } 2560 } 2561 2562 #ifndef NDEBUG 2563 2564 for(i = 0; i < nCols(); ++i) 2565 assert(newCols[i] == 0); 2566 2567 #endif 2568 2569 assert(SPxLPBase<R>::isConsistent()); 2570 2571 assert(set.num() == nRows() - oldRowNumber); 2572 addedRows(nRows() - oldRowNumber); 2573 addedCols(nCols() - oldColNumber); 2574 } 2575 2576 /// 2577 void doAddCol(const LPColBase<R>& col, bool scale = false) 2578 { 2579 int idx = nCols(); 2580 int oldRowNumber = nRows(); 2581 int newColScaleExp = 0; 2582 2583 LPColSetBase<R>::add(col); 2584 2585 if(thesense != MAXIMIZE) 2586 LPColSetBase<R>::maxObj_w(idx) *= -1; 2587 2588 SVectorBase<R>& vec = colVector_w(idx); 2589 2590 DataArray <int>& rowscaleExp = LPRowSetBase<R>::scaleExp; 2591 2592 // compute new column scaling factor and apply it to the bounds 2593 if(scale) 2594 { 2595 newColScaleExp = lp_scaler->computeScaleExp(vec, rowscaleExp); 2596 2597 if(upper(idx) < R(infinity)) 2598 upper_w(idx) = spxLdexp(upper_w(idx), - newColScaleExp); 2599 2600 if(lower(idx) > R(-infinity)) 2601 lower_w(idx) = spxLdexp(lower_w(idx), - newColScaleExp); 2602 2603 maxObj_w(idx) = spxLdexp(maxObj_w(idx), newColScaleExp); 2604 2605 LPColSetBase<R>::scaleExp[idx] = newColScaleExp; 2606 } 2607 2608 // now insert nonzeros to row file also 2609 for(int j = vec.size() - 1; j >= 0; --j) 2610 { 2611 int i = vec.index(j); 2612 2613 // apply new column and existing row scaling factors to new values in ColSet 2614 if(scale) 2615 vec.value(j) = spxLdexp(vec.value(j), newColScaleExp + rowscaleExp[i]); 2616 2617 R val = vec.value(j); 2618 2619 // create new rows if required 2620 if(i >= nRows()) 2621 { 2622 LPRowBase<R> empty; 2623 2624 for(int k = nRows(); k <= i; ++k) 2625 LPRowSetBase<R>::add(empty); 2626 } 2627 2628 assert(i < nRows()); 2629 LPRowSetBase<R>::add2(i, 1, &idx, &val); 2630 } 2631 2632 addedCols(1); 2633 addedRows(nRows() - oldRowNumber); 2634 } 2635 2636 /// 2637 void doAddCol(const R& objValue, const R& lowerValue, const SVectorBase<R>& colVec, 2638 const R& upperValue, bool scale = false) 2639 { 2640 int idx = nCols(); 2641 int oldRowNumber = nRows(); 2642 int newColScaleExp = 0; 2643 2644 LPColSetBase<R>::add(objValue, lowerValue, colVec, upperValue); 2645 2646 if(thesense != MAXIMIZE) 2647 LPColSetBase<R>::maxObj_w(idx) *= -1; 2648 2649 DataArray <int>& rowscaleExp = LPRowSetBase<R>::scaleExp; 2650 2651 // compute new column scaling factor and apply it to the bounds 2652 if(scale) 2653 { 2654 newColScaleExp = lp_scaler->computeScaleExp(colVec, rowscaleExp); 2655 2656 if(upper(idx) < R(infinity)) 2657 upper_w(idx) = spxLdexp(upper_w(idx), - newColScaleExp); 2658 2659 if(lower(idx) > R(-infinity)) 2660 lower_w(idx) = spxLdexp(lower_w(idx), - newColScaleExp); 2661 2662 maxObj_w(idx) = spxLdexp(maxObj_w(idx), newColScaleExp); 2663 2664 LPColSetBase<R>::scaleExp[idx] = newColScaleExp; 2665 } 2666 2667 SVectorBase<R>& vec = colVector_w(idx); 2668 2669 // now insert nonzeros to row file also 2670 for(int j = vec.size() - 1; j >= 0; --j) 2671 { 2672 int i = vec.index(j); 2673 2674 if(scale) 2675 vec.value(j) = spxLdexp(vec.value(j), newColScaleExp + rowscaleExp[i]); 2676 2677 R val = vec.value(j); 2678 2679 // create new rows if required 2680 if(i >= nRows()) 2681 { 2682 LPRowBase<R> empty; 2683 2684 for(int k = nRows(); k <= i; ++k) 2685 LPRowSetBase<R>::add(empty); 2686 } 2687 2688 assert(i < nRows()); 2689 LPRowSetBase<R>::add2(i, 1, &idx, &val); 2690 } 2691 2692 addedCols(1); 2693 addedRows(nRows() - oldRowNumber); 2694 } 2695 2696 /// 2697 void doAddCols(const LPColSetBase<R>& set, bool scale = false) 2698 { 2699 int i, j; 2700 int oldColNumber = nCols(); 2701 int oldRowNumber = nRows(); 2702 DataArray < int > newRows(nRows()); 2703 2704 if(&set != this) 2705 LPColSetBase<R>::add(set); 2706 2707 assert(LPColSetBase<R>::isConsistent()); 2708 assert(LPRowSetBase<R>::isConsistent()); 2709 2710 // count additional nonzeros per row 2711 for(i = nRows() - 1; i >= 0; --i) 2712 newRows[i] = 0; 2713 2714 for(i = set.num() - 1; i >= 0; --i) 2715 { 2716 const SVectorBase<R>& vec = set.colVector(i); 2717 2718 for(j = vec.size() - 1; j >= 0; --j) 2719 { 2720 // create new rows if required 2721 int l = vec.index(j); 2722 2723 if(l >= nRows()) 2724 { 2725 LPRowBase<R> empty; 2726 newRows.reSize(l + 1); 2727 2728 for(int k = nRows(); k <= l; ++k) 2729 { 2730 newRows[k] = 0; 2731 LPRowSetBase<R>::add(empty); 2732 } 2733 2734 } 2735 2736 assert(l < nRows()); 2737 newRows[l]++; 2738 } 2739 } 2740 2741 // extend rows as required 2742 for(i = 0; i < nRows(); ++i) 2743 { 2744 if(newRows[i] > 0) 2745 { 2746 int len = newRows[i] + rowVector(i).size(); 2747 LPRowSetBase<R>::xtend(i, len); 2748 rowVector_w(i).set_size(len); 2749 } 2750 } 2751 2752 // insert new elements to row file 2753 for(i = oldColNumber; i < nCols(); ++i) 2754 { 2755 // @todo: Is there a better way to write the following if, else? 2756 if(thesense == MAXIMIZE) 2757 { 2758 LPColSetBase<R>::maxObj_w(i) *= 1; 2759 } 2760 else // thesense is MINIMIZE = -1 2761 { 2762 LPColSetBase<R>::maxObj_w(i) *= -1; 2763 } 2764 2765 SVectorBase<R>& vec = colVector_w(i); 2766 int newColScaleExp = 0; 2767 2768 DataArray <int>& rowscaleExp = LPRowSetBase<R>::scaleExp; 2769 2770 // compute new column scaling factor and apply it to the bounds 2771 if(scale) 2772 { 2773 newColScaleExp = lp_scaler->computeScaleExp(vec, rowscaleExp); 2774 2775 if(upper(i) < R(infinity)) 2776 upper_w(i) = spxLdexp(upper_w(i), - newColScaleExp); 2777 2778 if(lower(i) > R(-infinity)) 2779 lower_w(i) = spxLdexp(lower_w(i), - newColScaleExp); 2780 2781 maxObj_w(i) = spxLdexp(maxObj_w(i), newColScaleExp); 2782 2783 LPColSetBase<R>::scaleExp[i] = newColScaleExp; 2784 } 2785 2786 for(j = vec.size() - 1; j >= 0; --j) 2787 { 2788 int k = vec.index(j); 2789 SVectorBase<R>& row = rowVector_w(k); 2790 int idx = row.size() - newRows[k]; 2791 assert(newRows[k] > 0); 2792 newRows[k]--; 2793 row.index(idx) = i; 2794 2795 // apply new column and existing row scaling factors to both ColSet and RowSet 2796 if(scale) 2797 vec.value(j) = spxLdexp(vec.value(j), newColScaleExp + rowscaleExp[k]); 2798 2799 row.value(idx) = vec.value(j); 2800 } 2801 } 2802 2803 #ifndef NDEBUG 2804 2805 for(i = 0; i < nRows(); ++i) 2806 assert(newRows[i] == 0); 2807 2808 #endif 2809 2810 assert(SPxLPBase<R>::isConsistent()); 2811 2812 assert(set.num() == nCols() - oldColNumber); 2813 addedCols(nCols() - oldColNumber); 2814 addedRows(nRows() - oldRowNumber); 2815 } 2816 2817 ///@} 2818 2819 public: 2820 2821 // ------------------------------------------------------------------------------------------------------------------ 2822 /**@name Constructors / Destructors */ 2823 ///@{ 2824 2825 /// Default constructor. 2826 SPxLPBase() 2827 { 2828 SPxLPBase<R>::clear(); // clear is virtual. 2829 2830 assert(isConsistent()); 2831 } 2832 2833 /// Destructor. 2834 virtual ~SPxLPBase() 2835 {} 2836 2837 /// Copy constructor. 2838 SPxLPBase(const SPxLPBase<R>& old) 2839 : LPRowSetBase<R>(old) 2840 , LPColSetBase<R>(old) 2841 , thesense(old.thesense) 2842 , offset(old.offset) 2843 , _isScaled(old._isScaled) 2844 , lp_scaler(old.lp_scaler) 2845 , spxout(old.spxout) 2846 { 2847 _tolerances = old._tolerances; 2848 assert(isConsistent()); 2849 } 2850 2851 /// Copy constructor. 2852 template < class S > 2853 SPxLPBase(const SPxLPBase<S>& old) 2854 : LPRowSetBase<R>(old) 2855 , LPColSetBase<R>(old) 2856 , thesense(old.thesense == SPxLPBase<S>::MINIMIZE ? SPxLPBase<R>::MINIMIZE : SPxLPBase<R>::MAXIMIZE) 2857 , offset(old.offset) 2858 , _isScaled(old._isScaled) 2859 , spxout(old.spxout) 2860 { 2861 lp_scaler = nullptr; 2862 _tolerances = old._tolerances; 2863 assert(isConsistent()); 2864 } 2865 2866 /// Assignment operator. 2867 SPxLPBase<R>& operator=(const SPxLPBase<R>& old) 2868 { 2869 if(this != &old) 2870 { 2871 LPRowSetBase<R>::operator=(old); 2872 LPColSetBase<R>::operator=(old); 2873 thesense = old.thesense; 2874 offset = old.offset; 2875 _isScaled = old._isScaled; 2876 lp_scaler = old.lp_scaler; 2877 spxout = old.spxout; 2878 _tolerances = old._tolerances; 2879 2880 assert(isConsistent()); 2881 } 2882 2883 return *this; 2884 } 2885 2886 /// Assignment operator. 2887 template < class S > 2888 SPxLPBase<R>& operator=(const SPxLPBase<S>& old) 2889 { 2890 if(this != (const SPxLPBase<R>*)(&old)) 2891 { 2892 // The value of old.lp_scaler has to be nullptr 2893 // Refer to issue #161 in soplex gitlab 2894 assert(old.lp_scaler == nullptr); 2895 2896 LPRowSetBase<R>::operator=(old); 2897 LPColSetBase<R>::operator=(old); 2898 thesense = (old.thesense) == SPxLPBase<S>::MINIMIZE ? SPxLPBase<R>::MINIMIZE : 2899 SPxLPBase<R>::MAXIMIZE; 2900 offset = R(old.offset); 2901 _isScaled = old._isScaled; 2902 _tolerances = old._tolerances; 2903 2904 // this may have un-intended consequences in the future 2905 lp_scaler = nullptr; 2906 spxout = old.spxout; 2907 2908 assert(isConsistent()); 2909 } 2910 2911 return *this; 2912 } 2913 2914 ///@} 2915 }; 2916 2917 } // namespace soplex 2918 2919 // For the general templated functions 2920 #include "spxlpbase_real.hpp" 2921 #include "spxlpbase_rational.hpp" 2922 2923 /* reset the SOPLEX_DEBUG flag to its original value */ 2924 #undef SOPLEX_DEBUG 2925 #ifdef SOPLEX_DEBUG_SPXLPBASE 2926 #define SOPLEX_DEBUG 2927 #undef SOPLEX_DEBUG_SPXLPBASE 2928 #endif 2929 2930 #endif // _SPXLPBASE_H_ 2931