// Copyright (C) 2002, International Business Machines // Corporation and others. All Rights Reserved. #include #include #include "CoinPresolveMatrix.hpp" #include "CoinPresolveFixed.hpp" #include "CoinPresolveDual.hpp" #include "CoinMessage.hpp" #include "CoinHelperFunctions.hpp" //#define PRESOLVE_TIGHTEN_DUALS 1 //#define PRESOLVE_DEBUG 1 // this looks for "dominated columns" // following ekkredc // rdmin == dwrow2 // rdmax == dwrow // this transformation is applied in: k4.mps, lp22.mps // make inferences using this constraint on reduced cost: // at optimality dj>0 ==> var must be at lb // dj<0 ==> var must be at ub // // This implies: // there is no lb ==> dj<=0 at optimality // there is no ub ==> dj>=0 at optimality /* This routine looks to be something of a work in progres. See the comment that begins with `Gack!'. And down in the bound propagation loop, why do we only work with variables with u_j = infty? The corresponding section of code for l_j = -infty is ifdef'd away. And why exclude the code protected by PRESOLVE_TIGHTEN_DUALS? */ const CoinPresolveAction *remove_dual_action::presolve(CoinPresolveMatrix *prob, const CoinPresolveAction *next) { 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_; double *csol = prob->sol_; int nrows = prob->nrows_; double *rlo = prob->rlo_; double *rup = prob->rup_; double *dcost = prob->cost_; const double maxmin = prob->maxmin_; const double ekkinf = 1e28; const double ekkinf2 = 1e20; const double ztoldj = prob->ztoldj_; double *rdmin = new double[nrows]; double *rdmax = new double[nrows]; // This combines the initialization of rdmin/rdmax to extreme values // (PRESOLVE_INF/-PRESOLVE_INF) with a version of the next loop specialized // for row slacks. // In this case, it is always the case that dprice==0.0 and coeff==1.0. int i; for ( i = 0; i < nrows; i++) { double sup = -rlo[i]; // slack ub; corresponds to cup[j] double slo = -rup[i]; // slack lb; corresponds to clo[j] bool no_lb = (slo <= -ekkinf); bool no_ub = (sup >= ekkinf); // here, dj==-row dual // the row slack has no lower bound, but it does have an upper bound, // then the reduced cost must be <= 0, so the row dual must be >= 0 rdmin[i] = (no_lb && !no_ub) ? 0.0 : -PRESOLVE_INF; rdmax[i] = (no_ub && !no_lb) ? 0.0 : PRESOLVE_INF; } // Look for col singletons and update bounds on dual costs // Take the min of the maxes and max of the mins int j; for ( j = 0; j= ekkinf); bool no_lb = (clo[j] <= -ekkinf); if (hincol[j] == 1 && // we need singleton cols that have exactly one bound (no_ub ^ no_lb)) { int row = hrow[mcstrt[j]]; double coeff = colels[mcstrt[j]]; PRESOLVEASSERT(fabs(coeff) > ZTOLDP); // I don't think the sign of dcost[j] matters // this row dual would make this col's reduced cost be 0 double dprice = maxmin * dcost[j] / coeff; // no_ub == !no_lb // no_ub ==> !(dj<0) // no_lb ==> !(dj>0) // I don't think the case where !no_ub has actually been tested if ((coeff > 0.0) == no_ub) { // coeff>0 ==> making the row dual larger would make dj *negative* // ==> dprice is an upper bound on dj if no *ub* // coeff<0 ==> making the row dual larger would make dj *positive* // ==> dprice is an upper bound on dj if no *lb* if (rdmax[row] > dprice) // reduced cost may be positive rdmax[row] = dprice; } else { // no lower bound if (rdmin[row] < dprice) // reduced cost may be negative rdmin[row] = dprice; } } } int *fixup_cols = new int[ncols]; int *fixdown_cols = new int[ncols]; #if PRESOLVE_TIGHTEN_DUALS double *djmin = new double[ncols]; double *djmax = new double[ncols]; #endif int nfixup_cols = 0; int nfixdown_cols = 0; while (1) { int tightened = 0; /* Perform duality tests */ for (int j = 0; j 0) { CoinBigIndex kcs = mcstrt[j]; CoinBigIndex kce = kcs + hincol[j]; // Number of infinite rows int nflagu = 0; int nflagl = 0; // Number of ordinary rows int nordu = 0; int nordl = 0; double ddjlo = maxmin * dcost[j]; double ddjhi = ddjlo; for (CoinBigIndex k = kcs; k < kce; k++) { int i = hrow[k]; double coeff = colels[k]; if (coeff > 0.0) { if (rdmin[i] >= -ekkinf2) { ddjhi -= coeff * rdmin[i]; nordu ++; } else { nflagu ++; } if (rdmax[i] <= ekkinf2) { ddjlo -= coeff * rdmax[i]; nordl ++; } else { nflagl ++; } } else { if (rdmax[i] <= ekkinf2) { ddjhi -= coeff * rdmax[i]; nordu ++; } else { nflagu ++; } if (rdmin[i] >= -ekkinf2) { ddjlo -= coeff * rdmin[i]; nordl ++; } else { nflagl ++; } } } // See if we may be able to tighten a dual if (cup[j]>ekkinf) { // dj can not be negative if (nflagu==1&&ddjhi<-ztoldj) { // We can make bound finite one way for (CoinBigIndex k = kcs; k < kce; k++) { int i = hrow[k]; double coeff = colels[k]; if (coeff > 0.0&&rdmin[i] < -ekkinf2) { // rdmax[i] has upper bound if (ddjhi ekkinf2 && newValue <= ekkinf2) { nflagl--; ddjlo -= coeff * newValue; } else if (rdmax[i] <= ekkinf2) { ddjlo -= coeff * (newValue-rdmax[i]); } rdmax[i] = newValue; tightened++; #if PRESOLVE_DEBUG printf("Col %d, row %d max pi now %g\n",j,i,rdmax[i]); #endif } } else if (coeff < 0.0 && rdmax[i] > ekkinf2) { // rdmin[i] has lower bound if (ddjhi= -ekkinf2) { nflagl--; ddjlo -= coeff * newValue; } else if (rdmax[i] >= -ekkinf2) { ddjlo -= coeff * (newValue-rdmin[i]); } rdmin[i] = newValue; tightened++; #if PRESOLVE_DEBUG printf("Col %d, row %d min pi now %g\n",j,i,rdmin[i]); #endif ddjlo = 0.0; } } } } else if (nflagl==0&&nordl==1&&ddjlo<-ztoldj) { // We may be able to tighten for (CoinBigIndex k = kcs; k < kce; k++) { int i = hrow[k]; double coeff = colels[k]; if (coeff > 0.0) { rdmax[i] += ddjlo/coeff; ddjlo =0.0; tightened++; #if PRESOLVE_DEBUG printf("Col %d, row %d max pi now %g\n",j,i,rdmax[i]); #endif } else if (coeff < 0.0 ) { rdmin[i] += ddjlo/coeff; ddjlo =0.0; tightened++; #if PRESOLVE_DEBUG printf("Col %d, row %d min pi now %g\n",j,i,rdmin[i]); #endif } } } } #if 0 if (clo[j]<-ekkinf) { // dj can not be positive if (ddjlo>ztoldj&&nflagl==1) { // We can make bound finite one way for (CoinBigIndex k = kcs; k < kce; k++) { int i = hrow[k]; double coeff = colels[k]; if (coeff < 0.0&&rdmin[i] < -ekkinf2) { // rdmax[i] has upper bound if (ddjlo>rdmax[i]*coeff+ztoldj) { double newValue = ddjlo/coeff; // re-compute hi if (rdmax[i] > ekkinf2 && newValue <= ekkinf2) { nflagu--; ddjhi -= coeff * newValue; } else if (rdmax[i] <= ekkinf2) { ddjhi -= coeff * (newValue-rdmax[i]); } rdmax[i] = newValue; tightened++; #if PRESOLVE_DEBUG printf("Col %d, row %d max pi now %g\n",j,i,rdmax[i]); #endif } } else if (coeff > 0.0 && rdmax[i] > ekkinf2) { // rdmin[i] has lower bound if (ddjlo>rdmin[i]*coeff+ztoldj) { double newValue = ddjlo/coeff; // re-compute lo if (rdmin[i] < -ekkinf2 && newValue >= -ekkinf2) { nflagu--; ddjhi -= coeff * newValue; } else if (rdmax[i] >= -ekkinf2) { ddjhi -= coeff * (newValue-rdmin[i]); } rdmin[i] = newValue; tightened++; #if PRESOLVE_DEBUG printf("Col %d, row %d min pi now %g\n",j,i,rdmin[i]); #endif } } } } else if (nflagu==0&&nordu==1&&ddjhi>ztoldj) { // We may be able to tighten for (CoinBigIndex k = kcs; k < kce; k++) { int i = hrow[k]; double coeff = colels[k]; if (coeff < 0.0) { rdmax[i] += ddjhi/coeff; ddjhi =0.0; tightened++; #if PRESOLVE_DEBUG printf("Col %d, row %d max pi now %g\n",j,i,rdmax[i]); #endif } else if (coeff > 0.0 ) { rdmin[i] += ddjhi/coeff; ddjhi =0.0; tightened++; #if PRESOLVE_DEBUG printf("Col %d, row %d min pi now %g\n",j,i,rdmin[i]); #endif } } } } #endif #if PRESOLVE_TIGHTEN_DUALS djmin[j] = (nflagl ? -PRESOLVE_INF : ddjlo); djmax[j] = (nflagu ? PRESOLVE_INF : ddjhi); #endif if (ddjlo > ztoldj && nflagl == 0&&!prob->colProhibited2(j)) { // dj>0 at optimality ==> must be at lower bound if (clo[j] <= -ekkinf) { prob->messageHandler()->message(COIN_PRESOLVE_COLUMNBOUNDB, prob->messages()) <status_ |= 2; break; } else { fixdown_cols[nfixdown_cols++] = j; //if (csol[j]-clo[j]>1.0e-7) //printf("down %d row %d nincol %d\n",j,hrow[mcstrt[j]],hincol[j]); // User may have given us feasible solution - move if simple if (csol&&csol[j]-clo[j]>1.0e-7&&hincol[j]==1) { double value_j = colels[mcstrt[j]]; double distance_j = csol[j]-clo[j]; int row=hrow[mcstrt[j]]; // See if another column can take value for (CoinBigIndex kk=mrstrt[row];kk0.0) { // k needs to increase double distance_k = cup[k]-csol[k]; movement = CoinMin((distance_j*value_j)/value_k,distance_k); } else { // k needs to decrease double distance_k = clo[k]-csol[k]; movement = CoinMax((distance_j*value_j)/value_k,distance_k); } csol[k] += movement; distance_j -= (movement*value_k)/value_j; csol[j] -= (movement*value_k)/value_j; if (distance_j<1.0e-7) break; } } } } } else if (ddjhi < -ztoldj && nflagu == 0&&!prob->colProhibited2(j)) { // dj<0 at optimality ==> must be at upper bound if (cup[j] >= ekkinf) { prob->messageHandler()->message(COIN_PRESOLVE_COLUMNBOUNDA, prob->messages()) <status_ |= 2; break; } else { fixup_cols[nfixup_cols++] = j; // User may have given us feasible solution - move if simple //if (cup[j]-csol[j]>1.0e-7) //printf("up %d row %d nincol %d\n",j,hrow[mcstrt[j]],hincol[j]); if (csol&&cup[j]-csol[j]>1.0e-7&&hincol[j]==1) { double value_j = colels[mcstrt[j]]; double distance_j = csol[j]-cup[j]; int row=hrow[mcstrt[j]]; // See if another column can take value for (CoinBigIndex kk=mrstrt[row];kk-1.0e-7) break; } } } } } } } // I don't know why I stopped doing this. #if PRESOLVE_TIGHTEN_DUALS const double *rowels = prob->rowels_; const int *hcol = prob->hcol_; const CoinBigIndex *mrstrt = prob->mrstrt_; int *hinrow = prob->hinrow_; // tighten row dual bounds, as described on p. 229 for (int i = 0; i < nrows; i++) { bool no_ub = (rup[i] >= ekkinf); bool no_lb = (rlo[i] <= -ekkinf); if ((no_ub ^ no_lb) == true) { CoinBigIndex krs = mrstrt[i]; CoinBigIndex kre = krs + hinrow[i]; double rmax = rdmax[i]; double rmin = rdmin[i]; // all row columns are non-empty for (CoinBigIndex k=krs; k ZTOLDP && djmax0 =ekkinf) { double bnd = djmax0 / coeff; if (rmax > bnd) { #if PRESOLVE_DEBUG printf("MAX TIGHT[%d,%d]: %g --> %g\n", i,hrow[k], rdmax[i], bnd); #endif rdmax[i] = rmax = bnd; tightened ++;; } } else if (coeff < -ZTOLDP && djmax0 = ekkinf) { double bnd = djmax0 / coeff ; if (rmin < bnd) { #if PRESOLVE_DEBUG printf("MIN TIGHT[%d,%d]: %g --> %g\n", i, hrow[k], rdmin[i], bnd); #endif rdmin[i] = rmin = bnd; tightened ++;; } } } else { // no_lb // dj must not be positive if (coeff > ZTOLDP && djmin0 > -PRESOLVE_INF && clo[icol]<=-ekkinf) { double bnd = djmin0 / coeff ; if (rmin < bnd) { #if PRESOLVE_DEBUG printf("MIN1 TIGHT[%d,%d]: %g --> %g\n", i, hrow[k], rdmin[i], bnd); #endif rdmin[i] = rmin = bnd; tightened ++;; } } else if (coeff < -ZTOLDP && djmin0 > -PRESOLVE_INF && clo[icol] <= -ekkinf) { double bnd = djmin0 / coeff ; if (rmax > bnd) { #if PRESOLVE_DEBUG printf("MAX TIGHT1[%d,%d]: %g --> %g\n", i,hrow[k], rdmax[i], bnd); #endif rdmax[i] = rmax = bnd; tightened ++;; } } } } } } #endif if (tightened<100||nfixdown_cols||nfixup_cols) break; #if PRESOLVE_TIGHTEN_DUALS else printf("DUAL TIGHTENED! %d\n", tightened); #endif } if (nfixup_cols) { #if PRESOLVE_DEBUG printf("NDUAL: %d\n", nfixup_cols); #endif next = make_fixed_action::presolve(prob, fixup_cols, nfixup_cols, false, next); } if (nfixdown_cols) { #if PRESOLVE_DEBUG printf("NDUAL: %d\n", nfixdown_cols); #endif next = make_fixed_action::presolve(prob, fixdown_cols, nfixdown_cols, true, next); } // If dual says so then we can make equality row // Also if cost is in right direction and only one binding row for variable // We may wish to think about giving preference to rows with 2 or 3 elements /* Gack! Ok, I can appreciate the thought here, but I'm seriously sceptical about writing canFix[0] before reading rdmin[0]. After that, we should be out of the interference zone for the typical situation where sizeof(double) is twice sizeof(int). */ int * canFix = (int *) rdmin; for ( i = 0; i < nrows; i++) { bool no_lb = (rlo[i] <= -ekkinf); bool no_ub = (rup[i] >= ekkinf); canFix[i]=0; if (no_ub && !no_lb ) { if ( rdmin[i]>0.0) canFix[i]=-1; else canFix[i]=-2; } else if (no_lb && !no_ub ) { if (rdmax[i]<0.0) canFix[i]=1; else canFix[i]=2; } } for (j = 0; j-ekkinf) bindingDown=-2; for (CoinBigIndex k = kcs; k < kce; k++) { int i = hrow[k]; if (abs(canFix[i])!=2) { bindingUp=-2; bindingDown=-2; break; } double coeff = colels[k]; if (coeff>0.0) { if (canFix[i]==2) { // binding up if (bindingUp==-1) bindingUp = i; else bindingUp = -2; } else { // binding down if (bindingDown==-1) bindingDown = i; else bindingDown = -2; } } else { if (canFix[i]==2) { // binding down if (bindingDown==-1) bindingDown = i; else bindingDown = -2; } else { // binding up if (bindingUp==-1) bindingUp = i; else bindingUp = -2; } } } double cost = maxmin * dcost[j]; if (bindingUp>-2&&cost<=0.0) { // might as well make equality if (bindingUp>=0) { canFix[bindingUp] /= 2; //So -2 goes to -1 etc //printf("fixing row %d to ub by %d\n",bindingUp,j); } else { //printf("binding up row by %d\n",j); } } else if (bindingDown>-2 &&cost>=0.0) { // might as well make equality if (bindingDown>=0) { canFix[bindingDown] /= 2; //So -2 goes to -1 etc //printf("fixing row %d to lb by %d\n",bindingDown,j); } else { //printf("binding down row by %d\n",j); } } } // can't fix if integer and non-unit coefficient //const double *rowels = prob->rowels_; //const int *hcol = prob->hcol_; //const CoinBigIndex *mrstrt = prob->mrstrt_; //int *hinrow = prob->hinrow_; const unsigned char *integerType = prob->integerType_; for ( i = 0; i < nrows; i++) { if (abs(canFix[i])==1) { CoinBigIndex krs = mrstrt[i]; CoinBigIndex kre = krs + hinrow[i]; for (CoinBigIndex k=krs; kclo[icol]&&integerType[icol]) { canFix[i]=0; // not safe } } } if (canFix[i]==1) { rlo[i]=rup[i]; prob->addRow(i); } else if (canFix[i]==-1) { rup[i]=rlo[i]; prob->addRow(i); } } delete[]rdmin; delete[]rdmax; delete[]fixup_cols; delete[]fixdown_cols; #if PRESOLVE_TIGHTEN_DUALS delete[]djmin; delete[]djmax; #endif if (prob->tuning_) { double thisTime=CoinCpuTime(); int droppedRows = prob->countEmptyRows() - startEmptyRows; int droppedColumns = prob->countEmptyCols() - startEmptyColumns; printf("CoinPresolveDual(1) - %d rows, %d columns dropped in time %g, total %g\n", droppedRows,droppedColumns,thisTime-startTime,thisTime-prob->startTime_); } return (next); }