/*! \legal Copyright (C) 2002, 2003, 2004. Lou Hafer, International Business Machines Corporation and others. All Rights Reserved. This file is a portion of the COIN/OSI interface for dylp. */ #ifdef COIN_HAS_DYLP #ifdef _MSC_VER /* Turn off compiler warning about long names */ # pragma warning(disable:4786) #endif // _MSC_VER /* Cut name lengths for readability. */ #define ODSI OsiDylpSolverInterface /* \file OsiDylpPresolve.cpp \brief Implementation of `native' presolve for dylp using CoinPresolve. `Native' presolve for dylp. The basic outline, implemented within OsiDylpSolverInterface::initialSolve, is as follows:
  1. Create an empty CoinPresolve object and load it from the ODSI object. Implemented by OsiDylpSolverInterface::initialisePresolve.
  2. Apply the presolve transforms. Implemented by OsiDylpSolverInterface::doPresolve.
  3. Consider whether it's worth proceeding --- if presolve had little or no effect, better to bail now and save the effort of loading the presolved system, postsolving, and reloading the original system. Implemented by OsiDylpSolverInterface::evalPresolve.
  4. Save the current constraint system. Implemented by OsiDylpSolverInterface::saveOriginalSys.
  5. Load the presolved problem. Implemented by OsiDylpSolverInterface::installPresolve
  6. Run dylp (cold start).
  7. Create a postsolve object from the presolve object. Destroy the presolve object in the process, after transferring its CoinPrePostsolveMatrix component to the CoinPostsolveMatrix object. Implemented by CoinPostsolveMatrix::assignPresolveToPostsolve
  8. Load the postsolve object with the basis for the presolved problem. This is done by creating a warm start object and using it to set the status in the postsolve object. Implemented by CoinPrePostsolveMatrix::setStatus
  9. Apply the postsolve transforms to convert the basis back to the original system. It's worth mentioning here that the only reason that postsolve transforms the constraint system is to keep the bookkeeping straight --- you'll notice there's no provision to extract the constraint system from the postsolve object. Postsolve will also handle things like primal and dual solution, but OsiDylp makes a call to dylp that will recreate all of this. Implemented by OsiDylpSolverInterface::doPostsolve.
  10. Restore the saved constraint system. Make the postsolved basis the active basis. Destroy the postsolve object. Implemented by OsiDylpSolverInterface::installPostsolve.
  11. Run dylp (warm start). This is (well, should be) a trivial (no-pivot) execution, simply to set up dylp's data structures. Given various bits of information loss and numerical twitchiness, a few pivots is reasonable.
Memory consumption is a nontrivial issue here. Midway through step 5), we have the original consys, plus column- and row-major copies in preObj_, and we're building a new consys. Now I see why it's worthwhile to destroy chunks of the data in preObj_. Give this some attention. A thought concerning presolve for resolve(). Here, there's some utility in returning a basis that's suitably modified for the presolved problem, and we have an optimal basis to start from. But presumably presolve should not change the status of variables that remain. Why not wait until presolve is completed, and modify the basis based on the information in originalColumns_ and originalRows_. */ //#define PRESOLVE_DEBUG 1 //#define PRESOLVE_CONSISTENCY 1 #include #include #include "OsiDylpMessages.hpp" #include "CoinPresolveMatrix.hpp" namespace { char sccsid[] UNUSED = "@(#)OsiDylpPresolve.cpp 1.6 11/06/04" ; char cvsid[] UNUSED = "$Id$" ; } #include "CoinPresolveEmpty.hpp" #include "CoinPresolveFixed.hpp" #include "CoinPresolveSingleton.hpp" #include "CoinPresolveDoubleton.hpp" #include "CoinPresolveTripleton.hpp" #include "CoinPresolveZeros.hpp" #include "CoinPresolveSubst.hpp" #include "CoinPresolveForcing.hpp" #include "CoinPresolveDual.hpp" #include "CoinPresolveTighten.hpp" #include "CoinPresolveUseless.hpp" #include "CoinPresolveDupcol.hpp" #include "CoinPresolveImpliedFree.hpp" #include "CoinPresolveIsolated.hpp" #if PRESOLVE_DEBUG || PRESOLVE_CONSISTENCY /* Debug stuff. This block covers the include and the namespace. */ #include "CoinPresolvePsdebug.hpp" namespace { // Anonymous namespace for debug routines /* A control routine for debug checks --- keeps down the clutter in doPresolve. Each time it's called, it prints a list of transforms applied since the last call, then does the requested checks. Checks are controlled by two magic numbers: chkMtx: 1<<0 consistency of column- and row-major representations 1<<1 check column-major representation for duplicate entries 1<<2 check row-major representation for duplicate entries 1<<3 check column-major representation for explicit zero 1<<4 check row-major representation for explicit zero 1<<5 check column-major bulk storage management links 1<<6 check row-major bulk storage management links chkSol: 1<<0 check primal variables for NaN/Inf 1<<1 check primal variables for feasibility (variable bounds) 1<<2 check primal variable status vs. solution and bounds 1<<8 check constraint lhs for NaN/Inf 1<<9 check constraint lhs for accuracy, feasibility (row bounds) These will likely grow in the future. Mstrix checks work only if PRESOLVE_CONSISTENCY is defined. Solution checks work only if PRESOLVE_DEBUG is defined. */ void check_and_tell (ioid chn, const CoinPresolveMatrix *preObj_, int chkMtx, int chkSol, const CoinPresolveAction *first, const CoinPresolveAction *&mark) { const CoinPresolveAction *current ; outfmt(chn,true,"PRESOLVE: ") ; if (first == 0 && mark == 0) { outfmt(chn,true,"checking prior to start.") ; } else if (first == mark) { outfmt(chn,true,"no transforms since last check.") ; } else { outfmt(chn,true,"applied") ; for (current = first ; current != mark && current != 0 ; current = current->next) { outfmt(chn,true," %s",current->name()) ; } mark = first ; } outfmt(chn,true,"\n") ; # if !PRESOLVE_CONSISTENCY chkMtx = 0 ; # endif # if !PRESOLVE_DEBUG chkSol = 0 ; # endif if (chkMtx&(1<<0)) { presolve_consistent(preObj_) ; } if (chkMtx&((1<<1)|(1<<2))) { bool chkCol = chkMtx&((1<<1)) ; bool chkRow = chkMtx&((1<<2)) ; presolve_no_dups(preObj_,chkCol,chkRow) ; } if (chkMtx&((1<<3)|(1<<4))) { bool chkCol = chkMtx&((1<<3)) ; bool chkRow = chkMtx&((1<<4)) ; presolve_no_zeros(preObj_,chkCol,chkRow) ; } if (chkMtx&((1<<5)|(1<<6))) { bool chkCol = chkMtx&((1<<5)) ; bool chkRow = chkMtx&((1<<6)) ; presolve_links_ok(preObj_,chkCol,chkRow) ; } /* As the routine is currently written, we need sol_ and colstat_ to do primal variable checks. In addition, we need acts_ to do constraint lhs checks. This could be more cleanly separated in presolve_check_sol with a bit of thought. */ if (chkSol) { bool colSolPresent = (preObj_->sol_ && preObj_->colstat_) ; bool rowActPresent = (preObj_->acts_) ; int chkColSol = 0 ; int chkRowAct = 0 ; int chkStatus = 0 ; if (colSolPresent) { chkColSol = chkSol&((1<<1)|(1<<0)) ; chkStatus = (chkSol&(1<<2))>>2 ; if (rowActPresent) { chkRowAct = (chkSol&((1<<8)|(1<<9)))>>8 ; } } if (chkColSol+chkStatus+chkRowAct > 0) { presolve_check_sol(preObj_,chkColSol,chkRowAct,chkStatus) ; } } return ; } } // end anonymous namespace for debug routines #endif // PRESOLVE_DEBUG || PRESOLVE_CONSISTENCY /* This routine creates and initializes the CoinPresolve object. keepIntegers: true to keep integer variable type information; false to treat all variables as continuous. */ CoinPresolveMatrix *ODSI::initialisePresolve (bool keepIntegers) { int m = getNumRows() ; int n = getNumCols() ; int numElems = getNumElements() ; CoinPresolveMatrix *preObj = new CoinPresolveMatrix(n,m,numElems) ; preObj->messageHandler()->setLogLevel(messageHandler()->logLevel()) ; const CoinPackedMatrix *pkMtx = getMatrixByCol() ; preObj->setMatrix(pkMtx) ; const double *dvec ; double val,exp ; dvec = getColLower() ; preObj->setColLower(dvec,-1) ; dvec = getColUpper() ; preObj->setColUpper(dvec,-1) ; dvec = getRowLower() ; preObj->setRowLower(dvec,-1) ; dvec = getRowUpper() ; preObj->setRowUpper(dvec,-1) ; dvec = getObjCoefficients() ; preObj->setCost(dvec,-1) ; val = getObjSense() ; preObj->setObjSense(val) ; getDblParam(OsiObjOffset,val) ; preObj->setObjOffset(val) ; getDblParam(OsiPrimalTolerance,val) ; exp = ((int) (.5 + log10((double) n))) - 2 ; if (exp > 0) { val *= pow(10,(double) exp) ; } preObj->setPrimalTolerance(val) ; preObj->setFeasibilityTolerance(1000*val) ; getDblParam(OsiDualTolerance,val) ; exp = ((int) (.5 + log10((double) m))) - 2 ; if (exp > 0) { val *= pow(10,(double) exp) ; } preObj->setDualTolerance(val) ; if (keepIntegers_) { unsigned char *variableType = new unsigned char [m] ; bool anyInteger = false ; for (int j = 0 ; j < m ; j++) { if (isInteger(j)) { variableType[j] = 1 ; anyInteger = true ; } else { variableType[j] = 0 ; } } preObj->setVariableType(variableType,n) ; preObj->setAnyInteger(anyInteger) ; delete[] variableType ; } else { preObj->setVariableType(false,n) ; preObj->setAnyInteger(false) ; } preObj->setAnyProhibited(false) ; return (preObj) ; } /* This routine orchestrates the application of presolve transforms to reduce the constraint system held in the presolve object (preObj_) to the presolved problem. Shamelessly adapted from OsiPresolve. Various comments questioning the wisdom of bits of code are scattered below. I've stripped the consistency checks so I can easily see structure while I'm composing the code. I will want to put them back in later. If I want to do that, I should consider moving them to CoinPresolveHelperFunctions.cpp or CoinPresolveDebug.cpp. */ void ODSI::doPresolve () { /* Status codes used by CoinPresolveMatrix. These really should be symbolic. Fake it here. */ const int feasibleStatus = 0 ; const int infeasibleStatus = 1 ; const int unboundedStatus = 2 ; /* Some definitions to make it easy to control the presolve transforms that are applied. Unless you're debugging, all of these should be true. */ const bool slackdoubleton = true ; const bool doubleton = true ; const bool tripleton = true ; const bool forcing = true ; const bool ifree = true ; const bool zerocost = true ; const bool dupcol = true ; const bool duprow = true ; const bool dual = true ; handler_->message(ODSI_PRESOL_STATS,messages_) << "Before presolve" << preObj_->getNumRows() << preObj_->getNumCols() << preObj_->getNumElems() << CoinMessageEol ; /* We start with no postsolve transforms. Assume we're feasible. */ postActions_ = 0 ; preObj_->setStatus(feasibleStatus) ; # if PRESOLVE_DEBUG || PRESOLVE_CONSISTENCY /* chkMtx and chkSol are magic numbers to control checks. See the documentation with check_and_tell. chkMtx is nonfunctional unless PRESOLVE_CONSISTENCY is defined. chkSol is nonfunctional unless PRESOLVE_DEBUG is defined. */ const CoinPresolveAction *dbgActionMark = 0 ; int chkMtx = 0x7f ; int chkSol = 0x00 ; check_and_tell(local_logchn, preObj_,chkMtx,chkSol,postActions_,dbgActionMark) ; # endif /* `Fix' variables before we get into the main transform loop. This transform physically removes from the problem variables that already have equal upper and lower bounds. */ postActions_ = make_fixed(preObj_,postActions_) ; # if PRESOLVE_DEBUG || PRESOLVE_CONSISTENCY check_and_tell(local_logchn, preObj_,chkMtx,chkSol,postActions_,dbgActionMark) ; # endif /* If we have integer variables, skip the presolve checks based on dual values. I'm unconvinced that integrality forces us to skip these, but that's probably because my understanding of the dual presolve checks is incomplete. */ bool doDualStuff = (preObj_->anyInteger() == false && dual == true) ; /* If we're feasible, set up for the main presolve transform loop by putting all (non-prohibited) rows and columns on the ToDo lists. Track the reduction in size of the constraint system (lastDropped). Also, CoinPresolveMatrix maintains a pass number which is used to control transform activity. Initialise it to 0. */ if (preObj_->status() == feasibleStatus) { preObj_->initColsToDo() ; preObj_->initRowsToDo() ; int colCnt,rowCnt ; int colsDropped,rowsDropped ; int lastRowsDropped = 0 ; int lastColsDropped = 0 ; double rowShrink,colShrink ; preObj_->setPass(0) ; /* Open the main presolve transform loop. There are two phases to each iteration: a minor loop that does inexpensive presolve transforms until convergence, then a single application of the expensive transforms. */ int currentPass ; for (currentPass = 0 ; currentPass < passLimit_ ; currentPass++) { const CoinPresolveAction *const lastAction = postActions_ ; # ifdef PRESOLVE_DEBUG outfmt(local_logchn,true,"Starting major pass %d\n",currentPass) ; # endif /* implied_free_action looks for a constraint i that implicitly bounds a variable x (think bounds tightening with arc consistency). If column j is singleton (sole nonzero coefficient is a) then row i and column j can be removed from the problem without complications. But if column j has two or more coefficients, complications ensue: we have to add multiples of row i to the other rows to eliminate x. fill_level puts an initial limit on the number of coefficients in a column. If there's exactly one other coefficient a, we can eliminate x from row k without fillin. After that, fillin will, in general, occur. */ int fill_level = 2 ; /* Apply inexpensive transforms until convergence. In the original code, the criteria for `convergence' are (lastAction == lastSimpleAction && fill_level > 0) The first checks to see if we actually did anything (in which case, new postsolve transforms have been added). The second catches a hack that (in a nutshell) allows us to know when we've run low on doubleton columns for implied_free_action and its subordinate subst_constraint_action. A limit of 2 (and *only* 2) will be converted to -3 in subst_constraint_action. When -3 is passed in, it triggers i_f_a and s_c_a to consider all columns, not just those in ColsToDo. The -3 will come back as 3, and will not change again. (Except for this special case, a call to i_f_a will not change fill_level.) I'm asking why I can't simply look at numColsToDo and numRowsToDo. All the transforms queue to both NextColsToDo and NextRowsToDo (some indirectly due to triggered transforms). Except for the special case of implied_free, they all scan from ColsToDo or RowsToDo. Seems to me that if we break on (numColsToDo == 0 && numRowsToDo == 0 && fill_level > 0) we'll actually save one round of function calls. */ while (1) { /* For some reason there's a hardwired limit on the number of slack_doubleton_actions performed per call. notFinished comes back true if the number of opportunities exceeds the limit. */ if (slackdoubleton == true) { bool notFinished = true ; while (notFinished) { postActions_ = slack_doubleton_action::presolve(preObj_, postActions_,notFinished) ; # if PRESOLVE_DEBUG || PRESOLVE_CONSISTENCY check_and_tell(local_logchn, preObj_,chkMtx,chkSol,postActions_,dbgActionMark) ; # endif } if (preObj_->status() != feasibleStatus) break ; } if (doubleton == true) { postActions_ = doubleton_action::presolve(preObj_,postActions_) ; # if PRESOLVE_DEBUG || PRESOLVE_CONSISTENCY check_and_tell(local_logchn, preObj_,chkMtx,chkSol,postActions_,dbgActionMark) ; # endif if (preObj_->status() != feasibleStatus) break ; } if (tripleton == true) { postActions_ = tripleton_action::presolve(preObj_,postActions_) ; # if PRESOLVE_DEBUG || PRESOLVE_CONSISTENCY check_and_tell(local_logchn, preObj_,chkMtx,chkSol,postActions_,dbgActionMark) ; # endif if (preObj_->status() != feasibleStatus) break ; } if (zerocost == true) { postActions_ = do_tighten_action::presolve(preObj_,postActions_) ; # if PRESOLVE_DEBUG || PRESOLVE_CONSISTENCY check_and_tell(local_logchn, preObj_,chkMtx,chkSol,postActions_,dbgActionMark) ; # endif if (preObj_->status() != feasibleStatus) break ; } if (forcing == true) { postActions_ = forcing_constraint_action::presolve(preObj_,postActions_) ; # if PRESOLVE_DEBUG || PRESOLVE_CONSISTENCY check_and_tell(local_logchn, preObj_,chkMtx,chkSol,postActions_,dbgActionMark) ; # endif if (preObj_->status() != feasibleStatus) break ; } if (ifree) { postActions_ = implied_free_action::presolve(preObj_,postActions_,fill_level) ; # if PRESOLVE_DEBUG || PRESOLVE_CONSISTENCY check_and_tell(local_logchn, preObj_,chkMtx,chkSol,postActions_,dbgActionMark) ; # endif if (preObj_->status() != feasibleStatus) break ; } /* Step preObj's ToDo arrays, moving queued rows and columns from NextToDo to ToDo and resetting changed flags. */ preObj_->stepColsToDo() ; preObj_->stepRowsToDo() ; /* Break out of the loop if no rows or columns are queued, unless we've triggered the special case for i_f_a. */ if (preObj_->numberColsToDo() == 0 && preObj_->numberRowsToDo() == 0 && fill_level > 0) break ; } /* That's it for the computationally cheap transforms. Reset the ToDo lists and try the more expensive transforms. */ preObj_->initColsToDo() ; preObj_->initRowsToDo() ; /* Attempt to fix variables at bound by propagating bounds on reduced costs. The routine does not consult the ToDo lists, so looking for the addition of postsolve actions is the only way to determine if another iteration might be useful. If we manage to fix variables, try to remove implied free variables but disable substitution of equalities into other constraints (fill_level = 0). */ if (doDualStuff) { for (int iter = 0 ; iter < 5 ; iter++) { const CoinPresolveAction *const marker = postActions_ ; postActions_ = remove_dual_action::presolve(preObj_,postActions_) ; # if PRESOLVE_DEBUG || PRESOLVE_CONSISTENCY check_and_tell(local_logchn, preObj_,chkMtx,chkSol,postActions_,dbgActionMark) ; # endif if (preObj_->status() != feasibleStatus) break ; if (ifree) { int fill_level = 0 ; postActions_ = implied_free_action::presolve(preObj_, postActions_,fill_level) ; # if PRESOLVE_DEBUG || PRESOLVE_CONSISTENCY check_and_tell(local_logchn, preObj_,chkMtx,chkSol,postActions_,dbgActionMark) ; # endif if (preObj_->status() != feasibleStatus) break ; } if (postActions_ == marker) break ; } } /* Check for duplicate columns and rows. */ if (dupcol) { postActions_ = dupcol_action::presolve(preObj_,postActions_) ; # if PRESOLVE_DEBUG || PRESOLVE_CONSISTENCY check_and_tell(local_logchn, preObj_,chkMtx,chkSol,postActions_,dbgActionMark) ; # endif if (preObj_->status() != feasibleStatus) break ; } if (duprow) { postActions_ = duprow_action::presolve(preObj_,postActions_) ; # if PRESOLVE_DEBUG || PRESOLVE_CONSISTENCY check_and_tell(local_logchn, preObj_,chkMtx,chkSol,postActions_,dbgActionMark) ; # endif if (preObj_->status() != feasibleStatus) break ; } /* See if we should continue. The requirement is that we've substantially reduced the problem. Checking postActions_ == lastAction is a quick test for any action at all. If there's action, see if it's enough to continue. For now, make the criteria a 5% reduction in rows or columns. */ rowsDropped = preObj_->countEmptyRows() ; colsDropped = preObj_->countEmptyCols() ; colCnt = preObj_->getNumCols() ; rowCnt = preObj_->getNumRows() ; rowShrink = ((double) (rowsDropped-lastRowsDropped))/rowCnt ; colShrink = ((double) (colsDropped-lastColsDropped))/colCnt ; handler_->message(ODSI_PRESOL_PASS,messages_) << currentPass << rowsDropped-lastRowsDropped << rowShrink << colsDropped-lastColsDropped << colShrink << CoinMessageEol ; if (postActions_ == lastAction) break ; if (rowShrink < .05 && colShrink < .05) break ; lastRowsDropped = rowsDropped ; lastColsDropped = colsDropped ; } } /* That's it, we're done with the presolve major loop. If we're still feasible, it's time for final cleanup: drop zero coefficients from the matrix, then drop empty rows and columns. */ if (preObj_->status() == feasibleStatus) { postActions_ = drop_zero_coefficients(preObj_,postActions_) ; # if PRESOLVE_DEBUG || PRESOLVE_CONSISTENCY check_and_tell(local_logchn, preObj_,chkMtx,chkSol,postActions_,dbgActionMark) ; # endif postActions_ = drop_empty_cols_action::presolve(preObj_,postActions_) ; # if PRESOLVE_DEBUG || PRESOLVE_CONSISTENCY chkMtx &= 0x2a ; check_and_tell(local_logchn, preObj_,chkMtx,chkSol,postActions_,dbgActionMark) ; # endif postActions_ = drop_empty_rows_action::presolve(preObj_,postActions_) ; # if PRESOLVE_DEBUG || PRESOLVE_CONSISTENCY check_and_tell(local_logchn, preObj_,chkMtx,chkSol,postActions_,dbgActionMark) ; # endif handler_->message(ODSI_PRESOL_STATS,messages_) << "After presolve" << preObj_->getNumRows() << preObj_->getNumCols() << preObj_->getNumElems() << CoinMessageEol ; } /* Whoops --- we've come up infeasible or unbounded. Let the user know. */ else { CoinMessageHandler *handler = preObj_->messageHandler() ; if (preObj_->status() == infeasibleStatus) { handler->message(COIN_PRESOLVE_INFEAS,preObj_->messages()) << preObj_->feasibilityTolerance_ << CoinMessageEol ; } else if (preObj_->status() == unboundedStatus) { handler->message(COIN_PRESOLVE_UNBOUND,preObj_->messages()) << CoinMessageEol ; } else { handler->message(COIN_PRESOLVE_INFEASUNBOUND,preObj_->messages()) << CoinMessageEol ; } } return ; } /* This routine has the responsibility of deciding whether presolve has done enough to make it worth installing. Or too much. For now, take the attitude that if we don't manage to eliminate any constraints, then presolve likely wasn't worth the effort. If we come back infeasible or unbounded, well, better let dylp check it on its own. At the other end of the spectrum, a B&C code will often pass in a problem in which a majority (or all) of the variables are fixed. Presolve may well reduce the problem to 0x0. In this case, there's literally nothing to do. */ bool ODSI::evalPresolve () { int origCons = getNumRows() ; int presolCons = preObj_->getNumRows() ; int presolVars = preObj_->getNumCols() ; int presolStatus = preObj_->status() ; /* Presolve returns infeasible or unbounded. */ if (presolStatus != 0) return (false) ; /* Presolve didn't manage to reduce the number of constraints. */ if (origCons <= presolCons) return (false) ; /* Presolve worked all too well; the constraint system is 0x0. I don't think it's possible for presolve to return 0xn (it should fix otherwise unconstrained bounded variables at some bound or declare unboundedness). But it never hurts to check. */ if (presolCons == 0 && presolVars == 0) return (false) ; return (true) ; } /* This routine saves the original constraint system. Trivial, but broken out in anticipation more functionality might be useful in the future. From an efficiency perspective, this is a wonderful idea. From a space perspective, it's not so wonderful. */ void ODSI::saveOriginalSys () { /* If we don't have a constraint system, we're deeply confused. Also, the value of lpprob->consys (if lpprob exists) must equal the value of consys at all times. */ assert(consys) ; savedConsys_ = consys ; consys = 0 ; if (lpprob) lpprob->consys = 0 ; /* Presolve should be invisible to clients, and one reasonable expectation is that pointers to structural vectors obtained from ODSI will remain constant across calls to the solver. However, the presolved problem is structurally different, so we need to hide these while dylp works on the presolved problem. */ saved_col_obj = _col_obj ; _col_obj = 0 ; saved_row_rhs = _row_rhs ; _row_rhs = 0 ; saved_row_lower = _row_lower ; _row_lower = 0 ; saved_row_upper = _row_upper ; _row_upper = 0 ; saved_row_sense = _row_sense ; _row_sense = 0 ; saved_row_range = _row_range ; _row_range = 0 ; saved_matrix_by_col = _matrix_by_col ; _matrix_by_col = 0 ; saved_matrix_by_row = _matrix_by_row ; _matrix_by_row = 0 ; return ; } /* This routine has the responsibility of installing a presolved problem in the ODSI object. Simply a matter of retrieving parameters and calling the right version of load_problem. We also need to replace the lpprob_struct. */ void ODSI::installPresolve () { int n = preObj_->getNumCols() ; int m = preObj_->getNumRows() ; const CoinBigIndex *colStarts = preObj_->getColStarts() ; const int *colLens = preObj_->getColLengths() ; const int *rowIndices = preObj_->getRowIndicesByCol() ; const double *coeffs = preObj_->getElementsByCol() ; const double *vlbs = preObj_->getColLower() ; const double *vubs = preObj_->getColUpper() ; const double *obj = preObj_->getCost() ; const double *clbs = preObj_->getRowLower() ; const double *cubs = preObj_->getRowUpper() ; double *rhs = new double[m] ; double *rhslow = new double[m] ; contyp_enum *ctyp = new contyp_enum[m] ; gen_rowparms(m,rhs,rhslow,ctyp,clbs,cubs) ; load_problem(n,m,colStarts,colLens,rowIndices,coeffs, vlbs,vubs,obj,ctyp,rhs,rhslow) ; construct_lpprob() ; delete[] rhs ; delete[] rhslow ; delete[] ctyp ; return ; } /* This routine is responsible for setting up for postsolve. It creates the postsolve object and does the major part of the initialisation by cannibalising the presolve object. It then loads solution information for the presolved problem: status, primal and dual solutions, and primal and dual activity. The call to assignPresolvetoPostsolve will destroy preObj and set the pointer to null. */ CoinPostsolveMatrix *ODSI::initialisePostsolve (CoinPresolveMatrix *&preObj) { CoinPostsolveMatrix *postObj = new CoinPostsolveMatrix(0,0,0) ; postObj->assignPresolveToPostsolve(preObj) ; CoinWarmStartBasis *ws = dynamic_cast(getWarmStart()) ; postObj->setStatus(ws) ; delete ws ; const double *temp ; temp = getColSolution() ; postObj->setColSolution(temp,-1) ; temp = getRowActivity() ; postObj->setRowActivity(temp,-1) ; temp = getRowPrice() ; postObj->setRowPrice(temp,-1) ; temp = getReducedCost() ; postObj->setReducedCost(temp,-1) ; return (postObj) ; } /* This routine is responsible for applying the list of postsolve actions in order to restore the original system. After each transform is applied, it's destroyed. */ void ODSI::doPostsolve () { handler_->message(ODSI_POSTSOL,messages_) << "start" << CoinMessageEol ; # if PRESOLVE_CONSISTENCY presolve_check_threads(postObj_) ; presolve_check_free_list(postObj_) ; # endif # if PRESOLVE_DEBUG presolve_check_sol(postObj_) ; presolve_check_reduced_costs(postObj_) ; presolve_check_duals(postObj_) ; # endif while (postActions_ != 0) { const CoinPresolveAction *postAction = postActions_ ; postActions_ = postAction->next ; handler_->message(ODSI_POSTSOL_ACT,messages_) << postAction->name() << CoinMessageEol ; postAction->postsolve(postObj_) ; # if PRESOLVE_CONSISTENCY presolve_check_threads(postObj_) ; presolve_check_free_list(postObj_) ; # endif # if PRESOLVE_DEBUG presolve_check_sol(postObj_) ; presolve_check_reduced_costs(postObj_) ; presolve_check_duals(postObj_) ; # endif delete postAction ; } handler_->message(ODSI_POSTSOL,messages_) << "complete" << CoinMessageEol ; return ; } /* This routine has the responsibility of restoring the original constraint system, setting the corresponding basis as the new active basis for this ODSI object. There's a really annoying problem here --- CoinWarmStartBasis does not support superbasic status, and postsolve will return superbasic status. */ void ODSI::installPostsolve () { /* Restore the constraint system --- trivially easy, just destroy the presolved system and replace it with the saved copy of the original. We need to claim ownership of the solver. */ if (flgon(lpprob->ctlopts,lpctlDYVALID)) dylp_owner = this ; destruct_problem(true) ; consys = savedConsys_ ; savedConsys_ = 0 ; /* Restore cached structural vectors. */ _col_obj = saved_col_obj ; saved_col_obj = 0 ; _row_rhs = saved_row_rhs ; saved_row_rhs = 0 ; _row_lower = saved_row_lower ; saved_row_lower = 0 ; _row_upper = saved_row_upper ; saved_row_upper = 0 ; _row_sense = saved_row_sense ; saved_row_sense = 0 ; _row_range = saved_row_range ; saved_row_range = 0 ; _matrix_by_col = saved_matrix_by_col ; saved_matrix_by_col = 0 ; _matrix_by_row = saved_matrix_by_row ; saved_matrix_by_row = 0 ; /* Set the basis. */ CoinWarmStart *postBasis = dynamic_cast(postObj_->getStatus()) ; if (setWarmStart(postBasis) == false) { throw CoinError("Could not install postsolve basis.", "installPostsolve","OsiDylpSolverInterface") ; } delete postBasis ; /* And destroy the postsolve object. */ delete postObj_ ; postObj_ = 0 ; return ; } /* This routine deletes data structures associated with presolve. It's a utility cleanup routine. */ void ODSI::destruct_presolve () { if (preObj_) { delete preObj_ ; preObj_ = 0 ; } if (postObj_) { delete postObj_ ; postObj_ = 0 ; } while (postActions_ != 0) { const CoinPresolveAction *postAction = postActions_ ; postActions_ = postAction->next ; delete postAction ; } /* Presence of savedConsys_ means that we called saveOriginalSys. */ if (savedConsys_) { consys_free(savedConsys_) ; savedConsys_ = 0 ; _col_obj = saved_col_obj ; saved_col_obj = 0 ; _row_rhs = saved_row_rhs ; saved_row_rhs = 0 ; _row_lower = saved_row_lower ; saved_row_lower = 0 ; _row_upper = saved_row_upper ; saved_row_upper = 0 ; _row_sense = saved_row_sense ; saved_row_sense = 0 ; _row_range = saved_row_range ; saved_row_range = 0 ; _matrix_by_col = saved_matrix_by_col ; saved_matrix_by_col = 0 ; _matrix_by_row = saved_matrix_by_row ; saved_matrix_by_row = 0 ; } return ; } #endif