// Copyright (C) 2000, International Business Machines // Corporation and others. All Rights Reserved. #if defined(_MSC_VER) // Turn off compiler warning about long names # pragma warning(disable:4786) #endif #include #include "CoinHelperFunctions.hpp" #include "CoinMpsIO.hpp" #include "CoinMessage.hpp" #include "CoinWarmStart.hpp" #include "OsiSolverInterface.hpp" #ifdef CBC_NEXT_VERSION #include "OsiSolverBranch.hpp" #endif #include "OsiCuts.hpp" #include "OsiRowCut.hpp" #include "OsiColCut.hpp" #include "OsiRowCutDebugger.hpp" #include "OsiAuxInfo.hpp" #include #include "CoinFinite.hpp" #include "CoinBuild.hpp" #include "CoinModel.hpp" #include "CoinLpIO.hpp" //############################################################################# // Hotstart related methods (primarily used in strong branching) // It is assumed that only bounds (on vars/constraints) can change between // markHotStart() and unmarkHotStart() //############################################################################# void OsiSolverInterface::markHotStart() { delete ws_; ws_ = getWarmStart(); } void OsiSolverInterface::solveFromHotStart() { setWarmStart(ws_); resolve(); } void OsiSolverInterface::unmarkHotStart() { delete ws_; ws_ = NULL; } //############################################################################# // Get indices of solution vector which are integer variables presently at // fractional values //############################################################################# OsiVectorInt OsiSolverInterface::getFractionalIndices(const double etol) const { const int colnum = getNumCols(); OsiVectorInt frac; CoinAbsFltEq eq(etol); for (int i = 0; i < colnum; ++i) { if (isInteger(i)) { const double ci = getColSolution()[i]; const double distanceFromInteger = ci - floor(ci + 0.5); if (! eq(distanceFromInteger, 0.0)) frac.push_back(i); } } return frac; } int OsiSolverInterface::getNumElements() const { return getMatrixByRow()->getNumElements(); } //############################################################################# // Methods for determining the type of column variable. // The method isContinuous() is presently implemented in the derived classes. // The methods below depend on isContinuous and the values // stored in the column bounds. //############################################################################# bool OsiSolverInterface::isBinary(int colIndex) const { if ( isContinuous(colIndex) ) return false; const double * cu = getColUpper(); const double * cl = getColLower(); if ( (cu[colIndex]== 1 || cu[colIndex]== 0) && (cl[colIndex]== 0 || cl[colIndex]==1) ) return true; else return false; } //----------------------------------------------------------------------------- bool OsiSolverInterface::isInteger(int colIndex) const { return !isContinuous(colIndex); } //----------------------------------------------------------------------------- bool OsiSolverInterface::isIntegerNonBinary(int colIndex) const { if ( isInteger(colIndex) && !isBinary(colIndex) ) return true; else return false; } //----------------------------------------------------------------------------- bool OsiSolverInterface::isFreeBinary(int colIndex) const { if ( isContinuous(colIndex) ) return false; const double * cu = getColUpper(); const double * cl = getColLower(); if ( (cu[colIndex]== 1) && (cl[colIndex]== 0) ) return true; else return false; } #if 0 // Return name of row if one exists or Rnnnnnnn std::string OsiSolverInterface::getRowName(int rowIndex) const { char name[9]; sprintf(name,"R%7.7d",rowIndex); std::string rowName(name); return rowName; } // Return name of column if one exists or Cnnnnnnn std::string OsiSolverInterface::getColName(int colIndex) const { char name[9]; sprintf(name,"C%7.7d",colIndex); std::string colName(name); return colName; } #endif //############################################################################# // Built-in (i.e., slow) methods for problem modification //############################################################################# void OsiSolverInterface::setObjCoeffSet(const int* indexFirst, const int* indexLast, const double* coeffList) { const int cnt = indexLast - indexFirst; for (int i = 0; i < cnt; ++i) { setObjCoeff(indexFirst[i], coeffList[i]); } } //----------------------------------------------------------------------------- void OsiSolverInterface::setColSetBounds(const int* indexFirst, const int* indexLast, const double* boundList) { while (indexFirst != indexLast) { setColBounds(*indexFirst, boundList[0], boundList[1]); ++indexFirst; boundList += 2; } } //----------------------------------------------------------------------------- void OsiSolverInterface::setRowSetBounds(const int* indexFirst, const int* indexLast, const double* boundList) { while (indexFirst != indexLast) { setRowBounds(*indexFirst, boundList[0], boundList[1]); ++indexFirst; boundList += 2; } } //----------------------------------------------------------------------------- void OsiSolverInterface::setRowSetTypes(const int* indexFirst, const int* indexLast, const char* senseList, const double* rhsList, const double* rangeList) { while (indexFirst != indexLast) { setRowType(*indexFirst++, *senseList++, *rhsList++, *rangeList++); } } //----------------------------------------------------------------------------- void OsiSolverInterface::setContinuous(const int* indices, int len) { for (int i = 0; i < len; ++i) { setContinuous(indices[i]); } } //----------------------------------------------------------------------------- void OsiSolverInterface::setInteger(const int* indices, int len) { for (int i = 0; i < len; ++i) { setInteger(indices[i]); } } /* Set the objective coefficients for all columns array [getNumCols()] is an array of values for the objective. This defaults to a series of set operations and is here for speed. */ void OsiSolverInterface::setObjective(const double * array) { int n=getNumCols(); for (int i=0;i=0); addCol(number, rows+start, elements+start, collb ? collb[i] : 0.0, colub ? colub[i] : infinity, obj ? obj[i] : 0.0); } } // Add columns from a build object void OsiSolverInterface::addCols(const CoinBuild & buildObject) { assert (buildObject.type()==1); // check correct int number = buildObject.numberColumns(); if (number) { CoinPackedVectorBase ** columns= new CoinPackedVectorBase * [number]; int iColumn; double * objective = new double [number]; double * lower = new double [number]; double * upper = new double [number]; for (iColumn=0;iColumnmessage(CLP_BAD_STRING_VALUES,messages_) // <message(CLP_COMPLICATED_MODEL,messages_) //<message(CLP_BAD_STRING_VALUES,messages_) // <message(CLP_COMPLICATED_MODEL,messages_) //<message(CLP_BAD_STRING_VALUES,messages_) // <=0); addRow(number, columns+start, elements+start, rowlb ? rowlb[i] : -infinity, rowub ? rowub[i] : infinity); } } //############################################################################# // Implement getObjValue in a simple way that the derived solver interfaces // can use if the choose. //############################################################################# double OsiSolverInterface::getObjValue() const { int nc = getNumCols(); const double * objCoef = getObjCoefficients(); const double * colSol = getColSolution(); double objOffset=0.0; getDblParam(OsiObjOffset,objOffset); // Compute dot product of objCoef and colSol and then adjust by offset // Jean-Sebastien pointed out this is overkill - lets just do it simply //double retVal = CoinPackedVector(nc,objCoef).dotProduct(colSol)-objOffset; double retVal = -objOffset; for ( int i=0 ; igetApplicationData(); } void OsiSolverInterface::setAuxiliaryInfo(OsiAuxInfo * auxiliaryInfo) { delete appDataEtc_; appDataEtc_ = auxiliaryInfo->clone(); } // Get pointer to auxiliary info object OsiAuxInfo * OsiSolverInterface::getAuxiliaryInfo() const { return appDataEtc_; } //############################################################################# // Methods related to Row Cut Debuggers //############################################################################# //------------------------------------------------------------------- // Activate Row Cut Debugger
// If the model name passed is on list of known models // then all cuts are checked to see that they do NOT cut // off the known optimal solution. void OsiSolverInterface::activateRowCutDebugger (const char * modelName) { delete rowCutDebugger_; rowCutDebugger_ = new OsiRowCutDebugger(*this,modelName); } /* Activate debugger using full solution array. Only integer values need to be correct. Up to user to get it correct. */ void OsiSolverInterface::activateRowCutDebugger (const double * solution) { delete rowCutDebugger_; rowCutDebugger_ = new OsiRowCutDebugger(*this,solution); } //------------------------------------------------------------------- // Get Row Cut Debugger
// If there is a row cut debugger object associated with // model AND if the known optimal solution is within the // current feasible region then a pointer to the object is // returned which may be used to test validity of cuts. // Otherwise NULL is returned const OsiRowCutDebugger * OsiSolverInterface::getRowCutDebugger() const { if (rowCutDebugger_&&rowCutDebugger_->onOptimalPath(*this)) { return rowCutDebugger_; } else { return NULL; } } // If you want to get debugger object even if not on optimal path then use this const OsiRowCutDebugger * OsiSolverInterface::getRowCutDebuggerAlways() const { if (rowCutDebugger_&&rowCutDebugger_->active()) { return rowCutDebugger_; } else { return NULL; } } //############################################################################# // Constructors / Destructor / Assignment //############################################################################# //------------------------------------------------------------------- // Default Constructor //------------------------------------------------------------------- OsiSolverInterface::OsiSolverInterface () : rowCutDebugger_(NULL), appDataEtc_(NULL), ws_(NULL), handler_(NULL), defaultHandler_(true) { setInitialData(); } // Set data for default constructor void OsiSolverInterface::setInitialData() { delete rowCutDebugger_; rowCutDebugger_ = NULL; delete ws_; ws_ = NULL; delete appDataEtc_; appDataEtc_ = new OsiAuxInfo(); if (defaultHandler_) { delete handler_; handler_ = NULL; } defaultHandler_=true; intParam_[OsiMaxNumIteration] = 9999999; intParam_[OsiMaxNumIterationHotStart] = 9999999; dblParam_[OsiDualObjectiveLimit] = DBL_MAX; dblParam_[OsiPrimalObjectiveLimit] = DBL_MAX; dblParam_[OsiDualTolerance] = 1e-6; dblParam_[OsiPrimalTolerance] = 1e-6; dblParam_[OsiObjOffset] = 0.0; strParam_[OsiProbName] = "OsiDefaultName"; strParam_[OsiSolverName] = "Unknown Solver"; handler_ = new CoinMessageHandler(); messages_ = CoinMessage(); // initialize all hints int hint; for (hint=OsiDoPresolveInInitial;hintclone(); if ( rhs.rowCutDebugger_!=NULL ) rowCutDebugger_ = new OsiRowCutDebugger(*rhs.rowCutDebugger_); defaultHandler_ = rhs.defaultHandler_; if (defaultHandler_) { handler_ = new CoinMessageHandler(*rhs.handler_); } else { handler_ = rhs.handler_; } messages_ = CoinMessages(rhs.messages_); CoinDisjointCopyN(rhs.intParam_, OsiLastIntParam, intParam_); CoinDisjointCopyN(rhs.dblParam_, OsiLastDblParam, dblParam_); CoinDisjointCopyN(rhs.strParam_, OsiLastStrParam, strParam_); CoinDisjointCopyN(rhs.hintParam_, OsiLastHintParam, hintParam_); CoinDisjointCopyN(rhs.hintStrength_, OsiLastHintParam, hintStrength_); } //------------------------------------------------------------------- // Destructor //------------------------------------------------------------------- OsiSolverInterface::~OsiSolverInterface () { // delete debugger - should be safe as only ever returned const delete rowCutDebugger_; rowCutDebugger_ = NULL; delete ws_; ws_ = NULL; delete appDataEtc_; if (defaultHandler_) { delete handler_; handler_ = NULL; } } //---------------------------------------------------------------- // Assignment operator //------------------------------------------------------------------- OsiSolverInterface & OsiSolverInterface::operator=(const OsiSolverInterface& rhs) { if (this != &rhs) { delete appDataEtc_; appDataEtc_ = rhs.appDataEtc_->clone(); delete rowCutDebugger_; if ( rhs.rowCutDebugger_!=NULL ) rowCutDebugger_ = new OsiRowCutDebugger(*rhs.rowCutDebugger_); else rowCutDebugger_ = NULL; CoinDisjointCopyN(rhs.intParam_, OsiLastIntParam, intParam_); CoinDisjointCopyN(rhs.dblParam_, OsiLastDblParam, dblParam_); CoinDisjointCopyN(rhs.strParam_, OsiLastStrParam, strParam_); CoinDisjointCopyN(rhs.hintParam_, OsiLastHintParam, hintParam_); CoinDisjointCopyN(rhs.hintStrength_, OsiLastHintParam, hintStrength_); delete ws_; ws_ = NULL; if (defaultHandler_) { delete handler_; handler_ = NULL; } defaultHandler_ = rhs.defaultHandler_; if (defaultHandler_) { handler_ = new CoinMessageHandler(*rhs.handler_); } else { handler_ = rhs.handler_; } } return *this; } //----------------------------------------------------------------------------- // Read mps files //----------------------------------------------------------------------------- int OsiSolverInterface::readMps(const char * filename, const char * extension) { CoinMpsIO m; m.setInfinity(getInfinity()); int numberErrors = m.readMps(filename,extension); handler_->message(COIN_SOLVER_MPS,messages_) <message(COIN_SOLVER_MPS,messages_) <message(COIN_SOLVER_MPS,messages_) <clone(); delete rowCutDebugger_; if ( rhs.rowCutDebugger_!=NULL ) rowCutDebugger_ = new OsiRowCutDebugger(*rhs.rowCutDebugger_); else rowCutDebugger_ = NULL; if (defaultHandler_) { delete handler_; } defaultHandler_ = rhs.defaultHandler_; if (defaultHandler_) { handler_ = new CoinMessageHandler(*rhs.handler_); } else { handler_ = rhs.handler_; } CoinDisjointCopyN(rhs.intParam_, OsiLastIntParam, intParam_); CoinDisjointCopyN(rhs.dblParam_, OsiLastDblParam, dblParam_); CoinDisjointCopyN(rhs.strParam_, OsiLastStrParam, strParam_); CoinDisjointCopyN(rhs.hintParam_, OsiLastHintParam, hintParam_); CoinDisjointCopyN(rhs.hintStrength_, OsiLastHintParam, hintStrength_); } // Resets as if default constructor void OsiSolverInterface::reset() { // Throw an exception throw CoinError("Needs coding for this interface", "reset", "OsiSolverInterface"); } /*Enables normal operation of subsequent functions. This method is supposed to ensure that all typical things (like reduced costs, etc.) are updated when individual pivots are executed and can be queried by other methods. says whether will be doing primal or dual */ void OsiSolverInterface::enableSimplexInterface(bool doingPrimal) {} //Undo whatever setting changes the above method had to make void OsiSolverInterface::disableSimplexInterface() {} /* Returns 1 if can just do getBInv etc 2 if has all OsiSimplex methods and 0 if it has none */ int OsiSolverInterface::canDoSimplexInterface() const { return 0; } /* Tells solver that calls to getBInv etc are about to take place. Underlying code may need mutable as this may be called from CglCut:;generateCuts which is const. If that is too horrific then each solver e.g. BCP or CBC will have to do something outside main loop. */ void OsiSolverInterface::enableFactorization() const { // Throw an exception throw CoinError("Needs coding for this interface", "enableFactorization", "OsiSolverInterface"); } // and stop void OsiSolverInterface::disableFactorization() const { // Throw an exception throw CoinError("Needs coding for this interface", "disableFactorization", "OsiSolverInterface"); } //Returns true if a basis is available bool OsiSolverInterface::basisIsAvailable() const { return false; /* // Throw an exception throw CoinError("Needs coding for this interface", "basisIsAvailable", "OsiSolverInterface"); */ } /* The following two methods may be replaced by the methods of OsiSolverInterface using OsiWarmStartBasis if: 1. OsiWarmStartBasis resize operation is implemented more efficiently and 2. It is ensured that effects on the solver are the same Returns a basis status of the structural/artificial variables At present as warm start i.e 0 free, 1 basic, 2 upper, 3 lower NOTE artificials are treated as +1 elements so for <= rhs artificial will be at lower bound if constraint is tight */ void OsiSolverInterface::getBasisStatus(int* cstat, int* rstat) const { // Throw an exception throw CoinError("Needs coding for this interface", "getBasisStatus", "OsiSolverInterface"); } /* Set the status of structural/artificial variables and factorize, update solution etc NOTE artificials are treated as +1 elements so for <= rhs artificial will be at lower bound if constraint is tight */ int OsiSolverInterface::setBasisStatus(const int* cstat, const int* rstat) { // Throw an exception throw CoinError("Needs coding for this interface", "setBasisStatus", "OsiSolverInterface"); } /* Perform a pivot by substituting a colIn for colOut in the basis. The status of the leaving variable is given in statOut. Where 1 is to upper bound, -1 to lower bound */ int OsiSolverInterface::pivot(int colIn, int colOut, int outStatus) { // Throw an exception throw CoinError("Needs coding for this interface", "pivot", "OsiSolverInterface"); } /* Obtain a result of the primal pivot Outputs: colOut -- leaving column, outStatus -- its status, t -- step size, and, if dx!=NULL, *dx -- primal ray direction. Inputs: colIn -- entering column, sign -- direction of its change (+/-1). Both for colIn and colOut, artificial variables are index by the negative of the row index minus 1. Return code (for now): 0 -- leaving variable found, -1 -- everything else? Clearly, more informative set of return values is required */ int OsiSolverInterface::primalPivotResult(int colIn, int sign, int& colOut, int& outStatus, double& t, CoinPackedVector* dx) { // Throw an exception throw CoinError("Needs coding for this interface", "primalPivotResult", "OsiSolverInterface"); } /* Obtain a result of the dual pivot (similar to the previous method) Differences: entering variable and a sign of its change are now the outputs, the leaving variable and its statuts -- the inputs If dx!=NULL, then *dx contains dual ray Return code: same */ int OsiSolverInterface::dualPivotResult(int& colIn, int& sign, int colOut, int outStatus, double& t, CoinPackedVector* dx) { // Throw an exception throw CoinError("Needs coding for this interface", "dualPivotResult", "OsiSolverInterface"); } //Get the reduced gradient for the cost vector c void OsiSolverInterface::getReducedGradient(double* columnReducedCosts, double * duals, const double * c) { // Throw an exception throw CoinError("Needs coding for this interface", "getReducedGradient", "OsiSolverInterface"); } /* Set a new objective and apply the old basis so that the reduced costs are properly updated */ void OsiSolverInterface::setObjectiveAndRefresh(double* c) { // Throw an exception throw CoinError("Needs coding for this interface", "setObjectiveAndRefresh", "OsiSolverInterface"); } //Get a row of the tableau (slack part in slack if not NULL) void OsiSolverInterface::getBInvARow(int row, double* z, double * slack) const { // Throw an exception throw CoinError("Needs coding for this interface", "getBInvARow", "OsiSolverInterface"); } //Get a row of the basis inverse void OsiSolverInterface::getBInvRow(int row, double* z) const { // Throw an exception throw CoinError("Needs coding for this interface", "getBInvRow", "OsiSolverInterface"); } //Get a column of the tableau void OsiSolverInterface::getBInvACol(int col, double* vec) const { // Throw an exception throw CoinError("Needs coding for this interface", "getBInvACol", "OsiSolverInterface"); } //Get a column of the basis inverse void OsiSolverInterface::getBInvCol(int col, double* vec) const { // Throw an exception throw CoinError("Needs coding for this interface", "getBInvCol", "OsiSolverInterface"); } /* Get basic indices (order of indices corresponds to the order of elements in a vector retured by getBInvACol() and getBInvCol()). */ void OsiSolverInterface::getBasics(int* index) const { // Throw an exception throw CoinError("Needs coding for this interface", "getBasics", "OsiSolverInterface"); } #ifdef CBC_NEXT_VERSION /* Solve 2**N (N==depth) problems and return solutions and bases. There are N branches each of which changes bounds on both sides as given by branch. The user should provide an array of (empty) results which will be filled in. See OsiSolveResult for more details (in OsiSolveBranch.?pp) but it will include a basis and primal solution. The order of results is left to right at feasible leaf nodes so first one is down, down, ..... Returns number of feasible leaves. Also sets number of solves done and number of iterations. This is provided so a solver can do faster. If forceBranch true then branch done even if satisfied */ int OsiSolverInterface::solveBranches(int depth,const OsiSolverBranch * branch, OsiSolverResult * result, int & numberSolves, int & numberIterations, bool forceBranch) { int * stack = new int [depth]; CoinWarmStart ** basis = new CoinWarmStart * [depth]; int iDepth; for (iDepth=0;iDepth=0) { if (iDepth==0) { // finished finished=true; break; } stack[iDepth]=-1; iDepth--; } if (!finished) { stack[iDepth]=1; } } } delete [] stack; for (iDepth=0;iDepth