/*! \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:
-
Create an empty CoinPresolve object and load it from the ODSI object.
Implemented by OsiDylpSolverInterface::initialisePresolve.
-
Apply the presolve transforms.
Implemented by OsiDylpSolverInterface::doPresolve.
-
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.
-
Save the current constraint system.
Implemented by OsiDylpSolverInterface::saveOriginalSys.
-
Load the presolved problem.
Implemented by OsiDylpSolverInterface::installPresolve
-
Run dylp (cold start).
-
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
-
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
-
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.
-
Restore the saved constraint system. Make the postsolved basis the
active basis. Destroy the postsolve object.
Implemented by OsiDylpSolverInterface::installPostsolve.
-
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