/*! \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 ODWSB OsiDylpWarmStartBasis #define CWSB CoinWarmStartBasis /*! \file OsiDylpWarmStartBasis.cpp \brief Implementation of warm start object for dylp. This file contains the implementation of a warm start object for dylp. It extends the CoinWarmStartBasis class by adding an explicit list of active constraints. When operating in dynamic mode, dylp needs to be told which constraints are active in the basis. For ease of use, constraint status is handled like variable status. There's an array, one entry per constraint, coded using CoinWarmStartBasis::Status. Inactive constraints are marked with isFree, active constraints with atLowerBound. Note that active/inactive is not equivalent to tight/loose. The default behaviour in dynamic mode is to purge constraints which are strictly loose, but dylp can be instructed to also purge tight constraints when the associated dual variable is zero. */ #include #include #include #include "OsiDylpWarmStartBasis.hpp" namespace { char sccsid[] UNUSED = "@(#)OsiDylpWarmStartBasis.cpp 1.7 11/06/04" ; char cvsid[] UNUSED = "$Id$" ; } using std::vector ; /* A macro to standardize the size of the status arrays. CWSB::Status is the status enum, and currently it occupies 2 bits, packed 4 per char (note the implicit assumption that a char is 1 byte). This macro calculates the number of bytes needed to hold ns status entires, rounded up to the nearest int. */ #define STATPERBYTE 4 #define STATALLOCUNIT sizeof(int) #define STATBYTES(zz_ns_zz) \ (((zz_ns_zz+STATALLOCUNIT*STATPERBYTE-1)/(STATALLOCUNIT*STATPERBYTE))* \ STATALLOCUNIT) /*! \defgroup ODWSBConstructorsDestructors ODWSB Constructors and Destructors \brief ODWSB Constructors and destructors ODWSB provides a default constructor, several variations on a copy constructor, a default destructor, and two variations on assignment. There are also routines to change the capacity of the warm start object. */ //@{ /*! Creates an empty basis. */ ODWSB::OsiDylpWarmStartBasis () : CWSB(), phase_(dyINV), constraintStatus_(0) { # ifdef PARANOIA checkBasis() ; # endif } /*! A true copy --- no data structures are shared with the original. */ ODWSB::OsiDylpWarmStartBasis (const OsiDylpWarmStartBasis &ws) : CWSB(ws), phase_(ws.phase_), constraintStatus_(0) { if (ws.constraintStatus_) { int constatsze = STATBYTES(getNumArtificial()) ; constraintStatus_ = new char[constatsze] ; memcpy(constraintStatus_,ws.constraintStatus_,constatsze) ; } # ifdef PARANOIA ws.checkBasis() ; checkBasis() ; # endif return ; } /*! `Virtual constructor' method, for when we need to duplicate the ODWSB object from code that doesn't know it has an ODSWB object. */ CoinWarmStart *ODWSB::clone () const { const ODWSB *odwsb_orig = dynamic_cast(this) ; ODWSB *odwsb_new = 0 ; if (odwsb_orig) { odwsb_new = new OsiDylpWarmStartBasis(*odwsb_orig) ; } # ifdef PARANOIA checkBasis() ; odwsb_new->checkBasis() ; # endif return (dynamic_cast(odwsb_new)) ; } /*! Construct by copying a set of status arrays (parameters preserved). If no constraint status is provided, the default is to declare all constraints active. The phase is set to dyPRIMAL1, which is safe for any basis. Consider assignBasisStatus(int,int,char*&,char*&) if the object should assume ownership. */ ODWSB::OsiDylpWarmStartBasis (int ns, int na, const char *sStat, const char *aStat, const char *cStat) : CWSB(ns,na,sStat,aStat), phase_(dyPRIMAL1), constraintStatus_(0) { int constatsze = STATBYTES(na) ; constraintStatus_ = new char[constatsze] ; /* If a status array is given, copy it in. If there's no array provided, assume all constraints are active. Set up a byte with the correct pattern and use it to initialize the constraint status. */ if (cStat) { memcpy(constraintStatus_,cStat,constatsze) ; } else { char byteActive = 0 ; int i ; for (i = 0 ; i <= 3 ; i++) setStatus(&byteActive,i,CWSB::atLowerBound) ; memset(constraintStatus_,byteActive,constatsze) ; } # ifdef PARANOIA checkBasis() ; # endif return ; } /*! Construct from a CoinWarmStartBasis. Much as the previous constructor, but we know we'll need to build the constraint status array. */ ODWSB::OsiDylpWarmStartBasis (const CoinWarmStartBasis &cwsb) : CWSB(cwsb), phase_(dyPRIMAL1), constraintStatus_(0) { int na = cwsb.getNumArtificial() ; int constatsze = STATBYTES(na) ; constraintStatus_ = new char[constatsze] ; char byteActive = 0 ; for (int i = 0 ; i <= 3 ; i++) setStatus(&byteActive,i,CWSB::atLowerBound) ; memset(constraintStatus_,byteActive,constatsze) ; # ifdef PARANOIA checkBasis() ; # endif return ; } /*! Assignment of structure (parameter preserved) */ OsiDylpWarmStartBasis& ODWSB::operator= (const OsiDylpWarmStartBasis &rhs) { if (this != &rhs) { CWSB::operator=(rhs) ; phase_ = rhs.phase_ ; delete[] constraintStatus_ ; if (rhs.constraintStatus_) { int constatsze = STATBYTES(getNumArtificial()) ; constraintStatus_ = new char[constatsze] ; memcpy(constraintStatus_,rhs.constraintStatus_,constatsze) ; } else { constraintStatus_ = 0 ; } } # ifdef PARANOIA checkBasis() ; # endif return *this ; } /*! Assignment of status arrays (parameters destroyed). The phase is set to dyPRIMAL1, which is safe for any basis. In this method the OsiDylpWarmStartBasis object assumes ownership of the arrays and upon return the argument pointers will be NULL. If copying is desirable, use the \link OsiDylpWarmStartBasis(int,int,const char*,const char*,const char*) array constructor \endlink or the \link operator=(const OsiDylpWarmStartBasis&) assignment operator \endlink. \note The pointers passed to this method will be freed using delete[], so they must be created using new[]. */ void ODWSB::assignBasisStatus (int ns, int na, char *&sStat, char *&aStat, char *&cStat) { CWSB::assignBasisStatus(ns,na,sStat,aStat) ; phase_ = dyPRIMAL1 ; delete[] constraintStatus_ ; constraintStatus_ = cStat ; cStat = 0 ; # ifdef PARANOIA checkBasis() ; # endif } /*! Assignment of status arrays (parameters destroyed). When no constraint status is provided, the default is to create an array which indicates all constraints are active. The phase is set to dyPRIMAL1, which is safe for any basis. In this method the OsiDylpWarmStartBasis object assumes ownership of the arrays and upon return the argument pointers will be NULL. If copying is desirable, use the \link OsiDylpWarmStartBasis(int,int,const char*,const char*,const char*) array constructor \endlink or the \link operator=(const OsiDylpWarmStartBasis&) assignment operator \endlink. \note The pointers passed to this method will be freed using delete[], so they must be created using new[]. */ void ODWSB::assignBasisStatus (int ns, int na, char *&sStat, char *&aStat) { int constatsze = STATBYTES(na) ; char byteActive = 0 ; int i ; CWSB::assignBasisStatus(ns,na,sStat,aStat) ; phase_ = dyPRIMAL1 ; delete[] constraintStatus_ ; constraintStatus_ = new char[constatsze] ; for (i = 0 ; i <= 3 ; i++) setStatus(&byteActive,i,CWSB::atLowerBound) ; memset(constraintStatus_,byteActive,constatsze) ; # ifdef PARANOIA checkBasis() ; # endif } ODWSB::~OsiDylpWarmStartBasis () { delete[] constraintStatus_ ; } /*! This routine sets the capacity of the warm start object. Any existing basis information is lost. The basis is inconsistent at the end of this routine: All constraints are active, but there are no basic variables (the status arrays are initialised to isFree by CWSB::setSize). */ void ODWSB::setSize (int ns, int na) { CWSB::setSize(ns,na) ; phase_ = dyINV ; delete[] constraintStatus_ ; if (na > 0) { int constatsze = STATBYTES(na) ; constraintStatus_ = new char[constatsze] ; char byteActive = 0 ; for (int i = 0 ; i <= 3 ; i++) setStatus(&byteActive,i,CWSB::atLowerBound) ; memset(constraintStatus_,byteActive,constatsze) ; } else { constraintStatus_ = 0 ; } return ; } /*! This routine sets the capacity of the warm start object. Any existing basis information is retained. If the new size is smaller than the existing basis, the existing basis is truncated to fit. If the new size is larger than the existing basis, additional structural variables are given the status nonbasic at lower bound, additional artificial variables are given the status basic, and additional constraints are given the status active. We need to allow for the possibility that the client has created an empty basis and is using resize instead of setSize to get the proper final size. The basis is consistent after this routine, even if it starts out as an empty basis. */ void ODWSB::resize (int numRows, int numCols) { int concnt = getNumArtificial() ; int varcnt = getNumStructural() ; bool empty ; if (concnt == 0 && varcnt == 0) { empty = true ; } else { empty = false ; assert(constraintStatus_) ; } CWSB::resize(numRows,numCols) ; if (numRows != concnt) { int oldsze = STATBYTES(concnt) ; int newsze = STATBYTES(numRows) ; char *newStat = new char[newsze] ; /* If the new capacity is smaller, truncate the existing basis. */ if (oldsze > newsze) { memcpy(newStat,constraintStatus_,newsze) ; } /* If the new capacity is larger, copy the existing basis, then mark the additional constraints as active. We need to be a bit careful here, because the allocated sizes are rounded out. The strategy is to (possibly) overlap one byte, correcting it after initialization. */ else { char byteActive = 0 ; int i,actualBytes ; actualBytes = concnt/STATPERBYTE ; for (i = 0 ; i <= 3 ; i++) setStatus(&byteActive,i,CWSB::atLowerBound) ; if (empty == false) { memcpy(newStat,constraintStatus_,oldsze) ; memset(newStat+actualBytes,byteActive,newsze-actualBytes) ; for (i = 0 ; i < concnt%STATPERBYTE ; i++) { setStatus(newStat+actualBytes,i, getStatus(constraintStatus_+actualBytes,i)) ; } } else { memset(newStat,byteActive,newsze) ; } } delete [] constraintStatus_ ; constraintStatus_ = newStat ; } # ifdef PARANOIA checkBasis() ; # endif return ; } /* compressRows takes an ordered list of target indices without duplicates and removes them, compressing the artificialStatus_ and constraintStatus_ arrays in place. It will fail spectacularly if the indices are not sorted. Use deleteRows if you need to preprocess the target indices to satisfy the conditions. */ void ODWSB::compressRows (int tgtCnt, const int *tgts) { /* Bail out if we're called with nothing to do. */ if (tgtCnt <= 0) return ; int i,keep,t,tgt,blkStart,blkEnd ; Status stati ; # ifdef COIN_DEBUG /* If we're debugging, scan to see if we're deleting nonbasic artificials. (In other words, are we deleting tight constraints?) Easiest to just do this up front as opposed to integrating it with the loops below. */ int nbCnt = 0 ; for (t = 0 ; t < tgtCnt ; t++) { i = tgts[t] ; stati = getStatus(artificialStatus_,i) ; if (status != CoinWarmStartBasis::basic) { nbCnt++ ; } } if (nbCnt > 0) { std::cout << nbCnt << " nonbasic artificials deleted." << std::endl ; } # endif /* We preserve all entries before the first target. Skip across consecutive target indices to establish the start of the first block to be retained. By testing for tgts[t]+1 >= tgts[t+1], we're robust against duplicate indices. */ keep = tgts[0] ; for (t = 0 ; t < tgtCnt-1 && tgts[t]+1 == tgts[t+1] ; t++) ; blkStart = tgts[t]+1 ; /* Outer loop works through the indices to be deleted. Inner loop copies runs of indices to keep. */ while (t < tgtCnt-1) { blkEnd = tgts[t+1]-1 ; for (i = blkStart ; i <= blkEnd ; i++) { stati = getStatus(artificialStatus_,i) ; setStatus(artificialStatus_,keep,stati) ; stati = getStatus(constraintStatus_,i) ; setStatus(constraintStatus_,keep++,stati) ; } for (t++ ; t < tgtCnt-1 && tgts[t]+1 == tgts[t+1] ; t++) ; blkStart = tgts[t]+1 ; } /* Finish off by copying from last deleted index to end of the status arrays. */ for (i = blkStart ; i < numArtificial_ ; i++) { stati = getStatus(artificialStatus_,i) ; setStatus(artificialStatus_,keep,stati) ; stati = getStatus(constraintStatus_,i) ; setStatus(constraintStatus_,keep++,stati) ; } numArtificial_ -= tgtCnt ; return ; } /* deleteRows takes an unordered list of target indices with duplicates and removes them from the basis. The strategy is to preprocesses the list into an ordered list without duplicates, suitable for compressRows. */ /*! Removal of a tight constraint with a nonbasic logical implies that some basic variable must be kicked out of the basis. There's no cheap, efficient way to choose this variable, so the choice is left to the client. */ /* To elaborate on the above comment: When a tight constraint is removed, new extreme points are exposed. There's a reasonable likelihood they'll be desireable points (since the tight constraint was most likely tight because it was blocking movement in a desireable direction). In essence, we want to take the next primal pivot and see what comes tight. That's a fair bit of work, and on balance best left to the client to decide if it's appropriate, or if something else should be done. */ void ODWSB::deleteRows (int rawTgtCnt, const int *rawTgts) { /* Bail out if we're called with nothing to do. */ if (rawTgtCnt <= 0) return ; int *tgts = new int[rawTgtCnt] ; memcpy(tgts,rawTgts,rawTgtCnt*sizeof(int)) ; int *first = &tgts[0] ; int *last = &tgts[rawTgtCnt] ; int *endUnique ; std::sort(first,last) ; endUnique = std::unique(first,last) ; int tgtCnt = endUnique-first ; compressRows(tgtCnt,tgts) ; delete [] tgts ; # ifdef PARANOIA checkBasis() ; # endif return ; } /* The good news is that CWSB::deleteColumns works just fine for ODWSB. */ //@} /*! \defgroup BasisInfo Methods to get and set basis information \brief Methods to get and set basis information */ //@{ /*! Count the number of active constraints by scanning the constraint status array. */ int ODWSB::numberActiveConstraints () const { int i ; int concnt = getNumArtificial() ; int actcnt = 0 ; for (i = 0 ; i < concnt ; i++) if (getStatus(constraintStatus_,i) == CWSB::atLowerBound) actcnt++ ; return (actcnt) ; } /*! \defgroup BasisDiff Basis `diff' methods \brief Methods to calculate and apply a `diff' between two bases. */ //@{ /*! The capabilities are limited by the CoinWarmStartBasis format: oldBasis can be no larger than newBasis (the basis pointed to by \c this), and the subset of newBasis covered by oldBasis is assumed to contain the identical set of logical and structural variables, in order. We use CWSB::generateDiff to deal with the status arrays for the logical and structural variables, then add an additional vector for constraint status. */ CoinWarmStartDiff *ODWSB::generateDiff (const CoinWarmStart *const oldCWS) const { /* Make sure the parameter is an OsiDylpWarmStartBasis. */ const OsiDylpWarmStartBasis *oldBasis = dynamic_cast(oldCWS) ; if (!oldBasis) { throw CoinError("Old basis not OsiDylpWarmStartBasis.", "generateDiff","OsiDylpWarmStartBasis") ; } const OsiDylpWarmStartBasis *newBasis = this ; # ifdef PARANOIA oldBasis->checkBasis() ; checkBasis() ; # endif /* Make sure newBasis is equal or bigger than oldBasis. Calculate the worst case number of diffs and allocate vectors to hold them. */ const int oldArtifCnt = oldBasis->getNumArtificial() ; const int newArtifCnt = newBasis->getNumArtificial() ; assert(newArtifCnt >= oldArtifCnt) ; /* Ok, we're good to go. Call CWSB::generateDiff to deal with the logical and structural arrays. */ const CoinWarmStartBasisDiff *cwsbDiff = dynamic_cast(CWSB::generateDiff(oldCWS)) ; /* Now generate diffs for the constraint array. */ int sizeOldArtif = (oldArtifCnt+15)>>4 ; int sizeNewArtif = (newArtifCnt+15)>>4 ; int maxBasisLength = sizeNewArtif ; unsigned int *diffNdx = new unsigned int [maxBasisLength]; unsigned int *diffVal = new unsigned int [maxBasisLength]; /* Scan the constraint status. For the portion of the status arrays which overlap, create diffs. Then add any additional status from newBasis. */ const unsigned int *oldStatus = reinterpret_cast(oldBasis->getConstraintStatus()) ; const unsigned int *newStatus = reinterpret_cast(newBasis->getConstraintStatus()) ; int numberChanged = 0 ; int i ; for (i = 0 ; i < sizeOldArtif ; i++) { if (oldStatus[i] != newStatus[i]) { diffNdx[numberChanged] = i ; diffVal[numberChanged++] = newStatus[i] ; } } for ( ; i < sizeNewArtif ; i++) { diffNdx[numberChanged] = i ; diffVal[numberChanged++] = newStatus[i] ; } /* Create the object of our desire. */ OsiDylpWarmStartBasisDiff *diff = new OsiDylpWarmStartBasisDiff(numberChanged,diffNdx,diffVal,cwsbDiff) ; /* Clean up and return. */ delete[] diffNdx ; delete[] diffVal ; delete cwsbDiff ; return (dynamic_cast(diff)) ; } /*! Update this basis by applying \p diff. It's assumed that the allocated capacity of the basis is sufficiently large. We use CWSB::applyDiff to deal with the logical and structural status vectors, then apply the diffs for the constraint status vector. Unfortunately, it's not really possible to verify the basis as diffs are applied. The typical approach is to blow out the basis to full size and apply diffs as we go. Intermediate stages can have inconsistencies because they aren't yet at full size. */ void ODWSB::applyDiff (const CoinWarmStartDiff *const cwsdDiff) { /* Make sure we have an OsiDylpWarmStartBasisDiff */ const OsiDylpWarmStartBasisDiff *diff = dynamic_cast(cwsdDiff) ; if (!diff) { throw CoinError("Diff not OsiDylpWarmStartBasisDiff.", "applyDiff","OsiDylpWarmStartBasis") ; } /* Call CWSB::applyDiff to deal with the logical and structural status. */ CWSB::applyDiff(cwsdDiff) ; /* Now fix up the constraint status array. Application of the diff is by straighforward replacement of words in the status array. */ const int numberChanges = diff->consze_ ; const unsigned int *diffNdxs = diff->condiffNdxs_ ; const unsigned int *diffVals = diff->condiffVals_ ; unsigned int *constraintStatus = reinterpret_cast(this->getConstraintStatus()) ; for (int i = 0 ; i < numberChanges ; i++) { unsigned int diffNdx = diffNdxs[i] ; unsigned int diffVal = diffVals[i] ; constraintStatus[diffNdx] = diffVal ; } return ; } //@} /*! \defgroup MiscUtil Miscellaneous Utilities \brief Miscellaneous utility functions */ //@{ /*! Print the warm start object in a (more or less) human-readable form. Intended for debugging. */ void ODWSB::print () const { char conlett[] = {'I','?','?','A'} ; char statlett[] = {'F','B','U','L'} ; int i,basic_logicals,basic_structurals ; std::cout << "ODWSB: " ; std::cout << getNumArtificial() << " constraints (" << numberActiveConstraints() << " active), " ; std::cout << getNumStructural() << " variables." << std::endl ; std::cout << "Rows: " ; for (i = 0 ; i < getNumArtificial() ; i++) { std::cout << conlett[getConStatus(i)] ; } std::cout << std::endl ; std::cout << " " ; basic_logicals = 0 ; for (i = 0 ; i < getNumArtificial() ; i++) { std::cout << statlett[getArtifStatus(i)] ; if (getArtifStatus(i) == CWSB::basic) basic_logicals++ ; } std::cout << std::endl ; std::cout << "Cols: " ; basic_structurals = 0 ; for (i = 0 ; i < getNumStructural() ; i++) { std::cout << statlett[getStructStatus(i)] ; if (getStructStatus(i) == CWSB::basic) basic_structurals++ ; } std::cout << std::endl << " basic: (" << basic_structurals << " + " << basic_logicals << ")" ; std::cout << std::endl ; # ifdef PARANOIA checkBasis() ; # endif return ; } /*! Check the basis to make sure it's properly formed. The convention adopted for inactive constraints in an ODWSB is that the logical should be basic. After subtracting the basic logicals associated with inactive constraints, there should be exactly enough basic variables for the active constraints. */ void ODWSB::checkBasis () const { int i,j ; bool retval = true ; int numBasicStruct,numBasicLog,numCons,numActCons ; Status conStat,logStat ; numBasicStruct = numberBasicStructurals() ; numBasicLog = 0 ; numCons = getNumArtificial() ; numActCons = numberActiveConstraints() ; /* Scan the constraint status vector. Check that the logical associated with an inactive constraint is basic. Count the number of basic logicals associated with active constraints --- these will be part of dylp's basis. The notion that the logical associated with an inactive constraint should be basic is a convention adopted by ODWSB when it must synthesize a full basis for use by COIN. Dylp simply doesn't see the problem --- the constraint is inactive and does not require a basic variable. If the logical is not basic, it indicates a logical error in the code. */ for (i = 0 ; i < numCons ; i++) { conStat = getConStatus(i) ; logStat = getArtifStatus(i) ; if (conStat == CWSB::isFree) { if (logStat != CWSB::basic) { std::cerr << "Basis error! Logical for inactive constraint " << i << " is nonbasic." << std::endl ; retval = false ; } } else if (conStat == CWSB::atLowerBound) { if (logStat == CWSB::basic) { numBasicLog++ ; } } else { std::cerr << "Basis error! Status of constraint " << i << " is " << conStat << ". Should be isFree or atLowerBound." << std::endl ; retval = false ; } } /* The number of basic variables (basic structural, plus basic logicals associated with active constraints) should equal the number of active constraints. ODSI can easily cope with too few basic variables, and it will happen on occasion, depending on how the client code implements fixing of basic variables. ODSI can't deal with too many basic variables (in general, there's no guarantee we can find a basic variable that's at bound). */ if (numBasicStruct+numBasicLog != numActCons) { if (numBasicStruct+numBasicLog < numActCons) { std::cerr << "Basis warning! " ; } else { std::cerr << "Basis error! " ; retval = false ; } std::cerr << numActCons << " active constraints but (" << numBasicStruct << "+" << numBasicLog << ") basic variables." << std::endl ; } if (retval == false) { std::cerr << "Basis consistency check failed!" << std::endl ; } return ; } //@} /* Routines for OsiDylpWarmStartBasisDiff */ /* Constructor given constraint diff data and a pointer to a CoinWarmStartBasisDiff object. */ OsiDylpWarmStartBasisDiff::OsiDylpWarmStartBasisDiff (int sze, const unsigned int *const diffNdxs, const unsigned int *const diffVals, const CoinWarmStartBasisDiff *const cwsbd) : CoinWarmStartBasisDiff(*cwsbd), consze_(sze), condiffNdxs_(0), condiffVals_(0) { if (sze > 0) { condiffNdxs_ = new unsigned int[sze] ; memcpy(condiffNdxs_,diffNdxs,sze*sizeof(unsigned int)) ; condiffVals_ = new unsigned int[sze] ; memcpy(condiffVals_,diffVals,sze*sizeof(unsigned int)) ; } return ; } /* Copy constructor */ OsiDylpWarmStartBasisDiff::OsiDylpWarmStartBasisDiff (const OsiDylpWarmStartBasisDiff &odwsbd) : CoinWarmStartBasisDiff(odwsbd), consze_(odwsbd.consze_), condiffNdxs_(0), condiffVals_(0) { if (odwsbd.consze_ > 0) { condiffNdxs_ = new unsigned int[odwsbd.consze_] ; memcpy(condiffNdxs_,odwsbd.condiffNdxs_, odwsbd.consze_*sizeof(unsigned int)) ; condiffVals_ = new unsigned int[odwsbd.consze_] ; memcpy(condiffVals_,odwsbd.condiffVals_, odwsbd.consze_*sizeof(unsigned int)) ; } return ; } /* Assignment --- for convenience when assigning objects containing CoinWarmStartBasisDiff objects. */ OsiDylpWarmStartBasisDiff& OsiDylpWarmStartBasisDiff::operator= (const OsiDylpWarmStartBasisDiff &rhs) { if (this != &rhs) { CoinWarmStartBasisDiff::operator=(rhs) ; if (consze_ > 0) { delete[] condiffNdxs_ ; delete[] condiffVals_ ; } consze_ = rhs.consze_ ; if (consze_ > 0) { condiffNdxs_ = new unsigned int[consze_] ; memcpy(condiffNdxs_,rhs.condiffNdxs_,consze_*sizeof(unsigned int)) ; condiffVals_ = new unsigned int[consze_] ; memcpy(condiffVals_,rhs.condiffVals_,consze_*sizeof(unsigned int)) ; } else { condiffNdxs_ = 0 ; condiffVals_ = 0 ; } } return (*this) ; } #endif // COIN_HAS_DYLP