// Copyright (C) 2002, International Business Machines // Corporation and others. All Rights Reserved. #include #include #include "CoinHelperFunctions.hpp" #include "CoinPresolveMatrix.hpp" #include "CoinPresolveEmpty.hpp" // for DROP_COL/DROP_ROW #include "CoinPresolveFixed.hpp" #include "CoinPresolveSingleton.hpp" #include "CoinPresolvePsdebug.hpp" #include "CoinMessage.hpp" // #define PRESOLVE_DEBUG 1 // #define PRESOLVE_SUMMARY 1 /* * Transfers singleton row bound information to the corresponding column bounds. * What I refer to as a row singleton would be called a doubleton * in the paper, since my terminology doesn't refer to the slacks. * In terms of the paper, we transfer the bounds of the slack onto * the variable (vii) and then "substitute" the slack out of the problem * (which is a noop). */ const CoinPresolveAction * slack_doubleton_action::presolve(CoinPresolveMatrix *prob, const CoinPresolveAction *next, bool & notFinished) { double startTime = 0.0; int startEmptyRows=0; int startEmptyColumns = 0; if (prob->tuning_) { startTime = CoinCpuTime(); startEmptyRows = prob->countEmptyRows(); startEmptyColumns = prob->countEmptyCols(); } double *colels = prob->colels_; int *hrow = prob->hrow_; CoinBigIndex *mcstrt = prob->mcstrt_; int *hincol = prob->hincol_; int ncols = prob->ncols_; double *clo = prob->clo_; double *cup = prob->cup_; double *rowels = prob->rowels_; const int *hcol = prob->hcol_; const CoinBigIndex *mrstrt = prob->mrstrt_; int *hinrow = prob->hinrow_; // int nrows = prob->nrows_; double *rlo = prob->rlo_; double *rup = prob->rup_; // If rowstat exists then all do unsigned char *rowstat = prob->rowstat_; double *acts = prob->acts_; double * sol = prob->sol_; // unsigned char * colstat = prob->colstat_; // const char *integerType = prob->integerType_; const double ztolzb = prob->ztolzb_; int numberLook = prob->numberRowsToDo_; int iLook; int * look = prob->rowsToDo_; bool fixInfeasibility = (prob->presolveOptions_&16384)!=0; action actions[MAX_SLACK_DOUBLETONS]; int nactions = 0; notFinished = false; int * fixed_cols = new int [ncols]; int nfixed_cols = 0; // this loop can apparently create new singleton rows; // I haven't bothered to detect this. // wasfor (int irow=0; irowaddCol(jcol); action *s = &actions[nactions]; nactions++; s->col = jcol; s->clo = clo[jcol]; s->cup = cup[jcol]; s->row = irow; s->rlo = rlo[irow]; s->rup = rup[irow]; s->coeff = coeff; } if (coeff < 0.0) { CoinSwap(lo, up); lo = -lo; up = -up; } if (lo <= -PRESOLVE_INF) lo = -PRESOLVE_INF; else { lo /= acoeff; if (lo <= -PRESOLVE_INF) lo = -PRESOLVE_INF; } if (up > PRESOLVE_INF) up = PRESOLVE_INF; else { up /= acoeff; if (up > PRESOLVE_INF) up = PRESOLVE_INF; } if (clo[jcol] < lo) clo[jcol] = lo; if (cup[jcol] > up) cup[jcol] = up; if (fabs(cup[jcol] - clo[jcol]) < ZTOLDP) { fixed_cols[nfixed_cols++] = jcol; } if (lo > up) { if (lo <= up + prob->feasibilityTolerance_||fixInfeasibility) { // If close to integer then go there double nearest = floor(lo+0.5); if (fabs(nearest-lo)<2.0*prob->feasibilityTolerance_) { lo = nearest; up = nearest; } else { lo = up; } clo[jcol] = lo; cup[jcol] = up; } else { prob->status_ |= 1; prob->messageHandler()->message(COIN_PRESOLVE_COLINFEAS, prob->messages()) <rlink_,irow) ; // just to make things squeeky rlo[irow] = 0.0; rup[irow] = 0.0; if (rowstat&&sol) { // update solution and basis int basisChoice=0; int numberBasic=0; double movement = 0 ; if (prob->columnIsBasic(jcol)) numberBasic++; if (prob->rowIsBasic(irow)) numberBasic++; if (sol[jcol] <= clo[jcol]+ztolzb) { movement = clo[jcol]-sol[jcol] ; sol[jcol] = clo[jcol]; prob->setColumnStatus(jcol,CoinPrePostsolveMatrix::atLowerBound); } else if (sol[jcol] >= cup[jcol]-ztolzb) { movement = cup[jcol]-sol[jcol] ; sol[jcol] = cup[jcol]; prob->setColumnStatus(jcol,CoinPrePostsolveMatrix::atUpperBound); } else { basisChoice=1; } if (numberBasic>1||basisChoice) prob->setColumnStatus(jcol,CoinPrePostsolveMatrix::basic); if (movement) { CoinBigIndex k; for (k = mcstrt[jcol] ; k < mcstrt[jcol]+hincol[jcol] ; k++) { int row = hrow[k]; if (hinrow[row]) acts[row] += movement*colels[k]; } } } /* Remove the row from this col in the col rep. It can happen that this will empty the column, in which case we can delink it. */ presolve_delete_from_col(irow,jcol,mcstrt,hincol,hrow,colels) ; if (hincol[jcol] == 0) { PRESOLVE_REMOVE_LINK(prob->clink_,jcol) ; } if (nactions >= MAX_SLACK_DOUBLETONS) { notFinished=true; break; } } } if (nactions) { # if PRESOLVE_SUMMARY printf("SINGLETON ROWS: %d\n", nactions); # endif action *save_actions = new action[nactions]; CoinMemcpyN(actions, nactions, save_actions); next = new slack_doubleton_action(nactions, save_actions, next); if (nfixed_cols) next = make_fixed_action::presolve(prob, fixed_cols, nfixed_cols, true, // arbitrary next); } delete [] fixed_cols; if (prob->tuning_) { double thisTime=CoinCpuTime(); int droppedRows = prob->countEmptyRows() - startEmptyRows ; int droppedColumns = prob->countEmptyCols() - startEmptyColumns; printf("CoinPresolveSingleton(2) - %d rows, %d columns dropped in time %g, total %g\n", droppedRows,droppedColumns,thisTime-startTime,thisTime-prob->startTime_); } return (next); } void slack_doubleton_action::postsolve(CoinPostsolveMatrix *prob) const { const action *const actions = actions_; const int nactions = nactions_; double *colels = prob->colels_; int *hrow = prob->hrow_; CoinBigIndex *mcstrt = prob->mcstrt_; int *hincol = prob->hincol_; int *link = prob->link_; // int ncols = prob->ncols_; double *clo = prob->clo_; double *cup = prob->cup_; double *rlo = prob->rlo_; double *rup = prob->rup_; double *sol = prob->sol_; double *rcosts = prob->rcosts_; double *acts = prob->acts_; double *rowduals = prob->rowduals_; unsigned char *colstat = prob->colstat_; // unsigned char *rowstat = prob->rowstat_; # if PRESOLVE_DEBUG char *rdone = prob->rdone_; # endif CoinBigIndex &free_list = prob->free_list_; const double ztolzb = prob->ztolzb_; for (const action *f = &actions[nactions-1]; actions<=f; f--) { int irow = f->row; double lo0 = f->clo; double up0 = f->cup; double coeff = f->coeff; int jcol = f->col; rlo[irow] = f->rlo; rup[irow] = f->rup; clo[jcol] = lo0; cup[jcol] = up0; acts[irow] = coeff * sol[jcol]; // add new element { CoinBigIndex k = free_list; assert(k >= 0 && k < prob->bulk0_) ; free_list = link[free_list]; hrow[k] = irow; colels[k] = coeff; link[k] = mcstrt[jcol]; mcstrt[jcol] = k; } hincol[jcol]++; // right? /* * Have to compute rowstat and rowduals, since we are adding the row. * that satisfy complentarity slackness. * * We may also have to modify colstat and rcosts if bounds * have been relaxed. */ if (!colstat) { // ???? rowduals[irow] = 0.0; } else { if (prob->columnIsBasic(jcol)) { /* variable is already basic, so slack in this row must be basic */ prob->setRowStatus(irow,CoinPrePostsolveMatrix::basic); rowduals[irow] = 0.0; /* hence no reduced costs change */ /* since the column was basic, it doesn't matter if it is now away from its bounds. */ /* the slack is basic and its reduced cost is 0 */ } else if ((fabs(sol[jcol] - lo0) <= ztolzb && rcosts[jcol] >= 0) || (fabs(sol[jcol] - up0) <= ztolzb && rcosts[jcol] <= 0)) { /* up against its bound but the rcost is still ok */ prob->setRowStatus(irow,CoinPrePostsolveMatrix::basic); rowduals[irow] = 0.0; /* hence no reduced costs change */ } else if (! (fabs(sol[jcol] - lo0) <= ztolzb) && ! (fabs(sol[jcol] - up0) <= ztolzb)) { /* variable not marked basic, * so was at a bound in the reduced problem, * but its bounds were tighter in the reduced problem, * so now it has to be made basic. */ prob->setColumnStatus(jcol,CoinPrePostsolveMatrix::basic); prob->setRowStatusUsingValue(irow); /* choose a value for rowduals[irow] that forces rcosts[jcol] to 0.0 */ /* new reduced cost = 0 = old reduced cost - new dual * coeff */ rowduals[irow] = rcosts[jcol] / coeff; rcosts[jcol] = 0.0; /* this value is provably of the right sign for the slack */ /* SHOULD SHOW THIS */ } else { /* the solution is at a bound, but the rcost is wrong */ prob->setColumnStatus(jcol,CoinPrePostsolveMatrix::basic); prob->setRowStatusUsingValue(irow); /* choose a value for rowduals[irow] that forcesd rcosts[jcol] to 0.0 */ rowduals[irow] = rcosts[jcol] / coeff; rcosts[jcol] = 0.0; /* this value is provably of the right sign for the slack */ /*rcosts[irow] = -rowduals[irow];*/ } } # if PRESOLVE_DEBUG rdone[irow] = SLACK_DOUBLETON; # endif } return ; } /* If we have a variable with one entry and no cost then we can transform the row from E to G etc. If there is a row objective region then we may be able to do this even with a cost. */ const CoinPresolveAction * slack_singleton_action::presolve(CoinPresolveMatrix *prob, const CoinPresolveAction *next, double * rowObjective) { double startTime = 0.0; int startEmptyRows=0; int startEmptyColumns = 0; if (prob->tuning_) { startTime = CoinCpuTime(); startEmptyRows = prob->countEmptyRows(); startEmptyColumns = prob->countEmptyCols(); } double *colels = prob->colels_; int *hrow = prob->hrow_; CoinBigIndex *mcstrt = prob->mcstrt_; int *hincol = prob->hincol_; //int ncols = prob->ncols_; double *clo = prob->clo_; double *cup = prob->cup_; double *rowels = prob->rowels_; int *hcol = prob->hcol_; CoinBigIndex *mrstrt = prob->mrstrt_; int *hinrow = prob->hinrow_; int nrows = prob->nrows_; double *rlo = prob->rlo_; double *rup = prob->rup_; // If rowstat exists then all do unsigned char *rowstat = prob->rowstat_; double *acts = prob->acts_; double * sol = prob->sol_; //unsigned char * colstat = prob->colstat_; const unsigned char *integerType = prob->integerType_; const double ztolzb = prob->ztolzb_; double *dcost = prob->cost_; //const double maxmin = prob->maxmin_; int numberLook = prob->numberColsToDo_; int iLook; int * look = prob->colsToDo_; int maxActions = CoinMin(numberLook,nrows/10); action * actions = new action[maxActions]; int nactions = 0; int * fixed_cols = new int [numberLook]; int nfixed_cols=0; int nWithCosts=0; double costOffset=0.0; for (iLook=0;iLookcolProhibited(iCol)) { double currentLower = rlo[iRow]; double currentUpper = rup[iRow]; if (!rowObjective) { if (dcost[iCol]) continue; } else if ((dcost[iCol]&¤tLower!=currentUpper)||rowObjective[iRow]) { continue; } double newLower=currentLower; double newUpper=currentUpper; if (coeff<0.0) { if (currentUpper>1.0e20||cup[iCol]>1.0e20) { newUpper=COIN_DBL_MAX; } else { newUpper -= coeff*cup[iCol]; if (newUpper>1.0e20) newUpper=COIN_DBL_MAX; } if (currentLower<-1.0e20||clo[iCol]<-1.0e20) { newLower=-COIN_DBL_MAX; } else { newLower -= coeff*clo[iCol]; if (newLower<-1.0e20) newLower=-COIN_DBL_MAX; } } else { if (currentUpper>1.0e20||clo[iCol]<-1.0e20) { newUpper=COIN_DBL_MAX; } else { newUpper -= coeff*clo[iCol]; if (newUpper>1.0e20) newUpper=COIN_DBL_MAX; } if (currentLower<-1.0e20||cup[iCol]>1.0e20) { newLower=-COIN_DBL_MAX; } else { newLower -= coeff*cup[iCol]; if (newLower<-1.0e20) newLower=-COIN_DBL_MAX; } } if (nactions==maxActions) { maxActions += CoinMin(numberLook-iLook,maxActions); action * temp = new action[maxActions]; memcpy(temp,actions,nactions*sizeof(action)); delete [] actions; actions=temp; } action *s = &actions[nactions]; nactions++; s->col = iCol; s->clo = clo[iCol]; s->cup = cup[iCol]; s->row = iRow; s->rlo = rlo[iRow]; s->rup = rup[iRow]; s->coeff = coeff; presolve_delete_from_row(iRow,iCol,mrstrt,hinrow,hcol,rowels) ; if (!hinrow[iRow]) PRESOLVE_REMOVE_LINK(prob->rlink_,iRow) ; // put row on stack of things to do next time prob->addRow(iRow); #ifdef PRINTCOST if (rowObjective&&dcost[iCol]) { printf("Singleton %d had coeff of %g in row %d - bounds %g %g - cost %g\n", iCol,coeff,iRow,clo[iCol],cup[iCol],dcost[iCol]); printf("Row bounds were %g %g now %g %g\n", rlo[iRow],rup[iRow],newLower,newUpper); } #endif // Row may be redundant but let someone else do that rlo[iRow]=newLower; rup[iRow]=newUpper; if (rowstat&&sol) { // update solution and basis if ((sol[iCol] < cup[iCol]-ztolzb&& sol[iCol] > clo[iCol]+ztolzb)||prob->columnIsBasic(iCol)) prob->setRowStatus(iRow,CoinPrePostsolveMatrix::basic); prob->setColumnStatusUsingValue(CoinPrePostsolveMatrix::atLowerBound); } // Force column to zero clo[iCol]=0.0; cup[iCol]=0.0; if (rowObjective&&dcost[iCol]) { rowObjective[iRow]=-dcost[iCol]/coeff; nWithCosts++; // adjust offset costOffset += currentLower*rowObjective[iRow]; prob->dobias_ -= currentLower*rowObjective[iRow]; } double movement; if (fabs(sol[iCol]-clo[iCol])clink_,iCol) ; //clo[iCol] = 0.0; //cup[iCol] = 0.0; fixed_cols[nfixed_cols++] = iCol; //presolve_consistent(prob); } } } if (nactions) { # if PRESOLVE_SUMMARY printf("SINGLETON COLS: %d\n", nactions); # endif printf("%d singletons, %d with costs - offset %g\n",nactions, nWithCosts, costOffset); action *save_actions = new action[nactions]; CoinMemcpyN(actions, nactions, save_actions); delete [] actions; next = new slack_singleton_action(nactions, save_actions, next); if (nfixed_cols) next = make_fixed_action::presolve(prob, fixed_cols, nfixed_cols, true, // arbitrary next); } delete [] fixed_cols; if (prob->tuning_) { double thisTime=CoinCpuTime(); int droppedRows = prob->countEmptyRows() - startEmptyRows ; int droppedColumns = prob->countEmptyCols() - startEmptyColumns; printf("CoinPresolveSingleton(3) - %d rows, %d columns dropped in time %g, total %g\n", droppedRows,droppedColumns,thisTime-startTime,thisTime-prob->startTime_); } return (next); } void slack_singleton_action::postsolve(CoinPostsolveMatrix *prob) const { const action *const actions = actions_; const int nactions = nactions_; double *colels = prob->colels_; int *hrow = prob->hrow_; CoinBigIndex *mcstrt = prob->mcstrt_; int *hincol = prob->hincol_; int *link = prob->link_; // int ncols = prob->ncols_; //double *rowels = prob->rowels_; //int *hcol = prob->hcol_; //CoinBigIndex *mrstrt = prob->mrstrt_; //int *hinrow = prob->hinrow_; double *clo = prob->clo_; double *cup = prob->cup_; double *rlo = prob->rlo_; double *rup = prob->rup_; double *sol = prob->sol_; double *rcosts = prob->rcosts_; double *acts = prob->acts_; double *rowduals = prob->rowduals_; double *dcost = prob->cost_; //const double maxmin = prob->maxmin_; unsigned char *colstat = prob->colstat_; // unsigned char *rowstat = prob->rowstat_; # if PRESOLVE_DEBUG char *rdone = prob->rdone_; # endif CoinBigIndex &free_list = prob->free_list_; const double ztolzb = prob->ztolzb_; for (const action *f = &actions[nactions-1]; actions<=f; f--) { int iRow = f->row; double lo0 = f->clo; double up0 = f->cup; double coeff = f->coeff; int iCol = f->col; rlo[iRow] = f->rlo; rup[iRow] = f->rup; clo[iCol] = lo0; cup[iCol] = up0; double movement=0.0; // acts was without coefficient - adjust acts[iRow] += coeff*sol[iCol]; if (acts[iRow]rup[iRow]+ztolzb) movement = rup[iRow]-acts[iRow]; double cMove = movement/coeff; sol[iCol] += cMove; acts[iRow] += movement; if (!dcost[iCol]) { // and to get column feasible cMove=0.0; if (sol[iCol]>cup[iCol]+ztolzb) cMove = cup[iCol]-sol[iCol]; else if (sol[iCol]columnIsBasic(iCol)) numberBasic++; if (prob->rowIsBasic(iRow)) numberBasic++; if (numberBasic>1) printf("odd in singleton\n"); if (sol[iCol]>clo[iCol]+ztolzb&&sol[iCol]setColumnStatus(iCol,CoinPrePostsolveMatrix::basic); prob->setRowStatusUsingValue(iRow); } else if (acts[iRow]>rlo[iRow]+ztolzb&&acts[iRow]setRowStatus(iRow,CoinPrePostsolveMatrix::basic); prob->setColumnStatusUsingValue(iCol); } else if (numberBasic) { prob->setRowStatus(iRow,CoinPrePostsolveMatrix::basic); prob->setColumnStatusUsingValue(iCol); } } } else { // must have been equality row assert (rlo[iRow]==rup[iRow]); double cost = rcosts[iCol]; // adjust for coefficient cost -= rowduals[iRow]*coeff; bool basic=true; if (fabs(sol[iCol]-cup[iCol])1.0e-6) basic=false; //printf("Singleton %d had coeff of %g in row %d (dual %g) - bounds %g %g - cost %g, (dj %g)\n", // iCol,coeff,iRow,rowduals[iRow],clo[iCol],cup[iCol],dcost[iCol],rcosts[iCol]); //if (prob->columnIsBasic(iCol)) //printf("column basic! "); //if (prob->rowIsBasic(iRow)) //printf("row basic "); //printf("- make column basic %s\n",basic ? "yes" : "no"); if (basic&&!prob->rowIsBasic(iRow)) { #ifdef PRINTCOST printf("Singleton %d had coeff of %g in row %d (dual %g) - bounds %g %g - cost %g, (dj %g - new %g)\n", iCol,coeff,iRow,rowduals[iRow],clo[iCol],cup[iCol],dcost[iCol],rcosts[iCol],cost); #endif if (prob->columnIsBasic(iCol)) printf("column basic!\n"); basic=false; } if (fabs(rowduals[iRow])>1.0e-6&&prob->rowIsBasic(iRow)) basic=true; if (basic) { // Make basic have zero reduced cost rowduals[iRow] = rcosts[iCol] / coeff; rcosts[iCol] = 0.0; } else { rcosts[iCol]=cost; //rowduals[iRow]=0.0; } if (colstat) { if (basic) { if (!prob->rowIsBasic(iRow)) { #if 0 // find column in row int jCol=-1; //for (CoinBigIndex j=mrstrt[iRow];jncols0_;k++) { CoinBigIndex j=mcstrt[k]; for (int i=0;isetColumnStatus(iCol,CoinPrePostsolveMatrix::basic); } prob->setRowStatusUsingValue(iRow); } else { //prob->setRowStatus(iRow,CoinPrePostsolveMatrix::basic); prob->setColumnStatusUsingValue(iCol); } } } #if 0 int nb=0; int kk; for (kk=0;kknrows_;kk++) if (prob->rowIsBasic(kk)) nb++; for (kk=0;kkncols_;kk++) if (prob->columnIsBasic(kk)) nb++; assert (nb==prob->nrows_); #endif // add new element { CoinBigIndex k = free_list; assert(k >= 0 && k < prob->bulk0_) ; free_list = link[free_list]; hrow[k] = iRow; colels[k] = coeff; link[k] = mcstrt[iCol]; mcstrt[iCol] = k; } hincol[iCol]++; // right? # if PRESOLVE_DEBUG rdone[iRow] = SLACK_SINGLETON; # endif } return ; }