/**Clean cut 2, different algorithm. First check the dynamic of the cut if < maxRatio scale to a biggest coef of 1 otherwise scale it so that biggest coeff is 1 and try removing tinys ( < 1/maxRatio) either succeed or fail */ int Validator::cleanCut2(OsiRowCut & aCut, const double * solCut, const OsiSolverInterface &si, const CglParam &/* par */, const double * origColLower, const double * origColUpper) const { /** Compute fill-in in si */ int numcols = si.getNumCols(); // int numrows = si.getNumRows(); const double * colLower = (origColLower) ? origColLower : si.getColLower(); const double * colUpper = (origColUpper) ? origColUpper : si.getColUpper(); int maxNnz = static_cast<int> ( maxFillIn_ * static_cast<double> (numcols)); double rhs = aCut.lb(); assert (aCut.ub()> 1e50); CoinPackedVector *vec = const_cast<CoinPackedVector *>(&aCut.row()); // vec->sortIncrIndex(); int * indices = vec->getIndices(); double * elems = vec->getElements(); int n = vec->getNumElements(); if (n==0) { numRejected_[EmptyCut]++; return EmptyCut; } /** First compute violation if it is too small exit */ double violation = aCut.violated(solCut); if (violation < minViolation_) return 1; /** Now relax get dynamic and remove tiny elements */ int offset = 0; rhs -= 1e-10; double smallest = fabs(rhs); double biggest = smallest; double veryTiny = 1e-20; for (int i = 0 ; i < n ; i++) { double val = fabs(elems[i]); if (val > veryTiny) //tiny should be very very small { smallest = std::min(val,smallest); biggest = std::max (val,biggest); } } if (biggest > 1e9) { #ifdef DEBUG std::cout<<"Whaooo "<<biggest/smallest<<std::endl; #endif numRejected_[BigDynamic]++; return BigDynamic; } //rescale the cut so that biggest is 1e1. double toBeBiggest = rhsScale_; rhs *= (toBeBiggest / biggest); toBeBiggest /= biggest; for (int i = 0 ; i < n ; i++) { elems[i] *= toBeBiggest; } if (biggest > maxRatio_ * smallest) //we have to remove some small coefficients { double myTiny = biggest * toBeBiggest / maxRatio_; veryTiny *= toBeBiggest ; for (int i = 0 ; i < n ; i++) { double val = fabs(elems[i]); if (val < myTiny) { if (val< veryTiny) { offset++; continue; } int & iCol = indices[i]; if (elems[i]>0. && colUpper[iCol] < 1000.) { offset++; rhs -= elems[i] * colUpper[iCol]; elems[i]=0; } else if (elems[i]<0. && colLower[iCol] > -1000.) { offset++; rhs -= elems[i] * colLower[iCol]; elems[i]=0.; } else { numRejected_[SmallCoefficient]++; return SmallCoefficient; } } else //Not a small coefficient keep it { if (offset) //if offset is zero current values are ok { int i2 = i - offset; indices[i2] = indices[i]; elems[i2] = elems[i]; } } } } if ((n - offset) > maxNnz) { numRejected_[DenseCut] ++; return DenseCut; } if (offset) vec->truncate(n - offset); if (vec->getNumElements() == 0 ) { numRejected_[EmptyCut]++; return EmptyCut; } /** recheck violation */ aCut.setLb(rhs); violation = aCut.violated(solCut); if (violation < minViolation_) { numRejected_[SmallViolation]++; return SmallViolation; } assert(fabs(rhs)<1e09); return NoneAccepted; }
/** Clean an OsiCut \return 1 if min violation is too small \return 2 if small coefficient can not be removed \return 3 if dynamic is too big \return 4 if too many non zero element*/ int Validator::cleanCut(OsiRowCut & aCut, const double * solCut, const OsiSolverInterface &si, const CglParam& par, const double * origColLower, const double * origColUpper) const { /** Compute fill-in in si */ int numcols = si.getNumCols(); const double * colLower = (origColLower) ? origColLower : si.getColLower(); const double * colUpper = (origColUpper) ? origColUpper : si.getColUpper(); int maxNnz = static_cast<int> (maxFillIn_ * static_cast<double> (numcols)); double rhs = aCut.lb(); assert (aCut.ub()> 1e50); CoinPackedVector *vec = const_cast<CoinPackedVector *>(&aCut.row()); int * indices = vec->getIndices(); double * elems = vec->getElements(); int n = vec->getNumElements(); /** First compute violation if it is too small exit */ double violation = aCut.violated(solCut); if (violation < minViolation_) return 1; /** Now relax get dynamic and remove tiny elements */ int offset = 0; rhs -= 1e-8; double smallest = 1e100; double biggest = 0; for (int i = 0 ; i < n ; i++) { double val = fabs(elems[i]); if (val <= par.getEPS()) //try to remove coef { if (val>0 && val<1e-20) { offset++; continue; throw; } if (val==0) { offset++; continue; } int & iCol = indices[i]; if (elems[i]>0. && colUpper[iCol] < 10000.) { offset++; rhs -= elems[i] * colUpper[iCol]; elems[i]=0; } else if (elems[i]<0. && colLower[iCol] > -10000.) { offset++; rhs -= elems[i] * colLower[iCol]; elems[i]=0.; } else { #ifdef DEBUG std::cout<<"Small coefficient : "<<elems[i]<<" bounds : ["<<colLower[iCol]<<", "<<colUpper[iCol]<<std::endl; #endif numRejected_[SmallCoefficient]++; return SmallCoefficient; } } else //Not a small coefficient keep it { smallest = std::min(val,smallest); biggest = std::max (val,biggest); if (biggest > maxRatio_ * smallest) { #ifdef DEBUG std::cout<<"Whaooo "<<biggest/smallest<<std::endl; #endif numRejected_[BigDynamic]++; return BigDynamic; } if (offset) //if offset is zero current values are ok otherwise translate { int i2 = i - offset; indices[i2] = indices[i]; elems[i2] = elems[i]; } } } if ((n - offset) > maxNnz) { numRejected_[DenseCut] ++; return DenseCut; } if (offset == n) { numRejected_[EmptyCut]++; return EmptyCut; } if (offset) vec->truncate(n - offset); indices = vec->getIndices(); elems = vec->getElements(); n = vec->getNumElements(); aCut.setLb(rhs); violation = aCut.violated(solCut); if (violation < minViolation_) { numRejected_[SmallViolation]++; return SmallViolation; } return NoneAccepted; }
SCIP_RETCODE SCIPconshdlrBenders::sepaBenders( SCIP * scip, SCIP_CONSHDLR * conshdlr, SCIP_SOL * sol, whereFrom where, SCIP_RESULT * result) { OsiCuts cs; /**< Benders cut placeholder */ SCIP_Real * vals = NULL; /**< current solution */ #if 1 if (scip_checkpriority_ < 0) { /** consider incumbent solutions only */ double primObj = SCIPgetPrimalbound(scip); double currObj = SCIPgetSolOrigObj(scip, sol); if (SCIPisLT(scip, primObj, currObj)) { DSPdebugMessage(" -> primObj %e currObj %e\n", primObj, currObj); return SCIP_OKAY; } } #endif /** allocate memory */ SCIP_CALL(SCIPallocMemoryArray(scip, &vals, nvars_)); /** get current solution */ SCIP_CALL(SCIPgetSolVals(scip, sol, nvars_, vars_, vals)); /** TODO The following filter does not work, meaning that it provides suboptimal solution. * I do not know the reason. */ #if 0 double maxviol = 1.e-10; for (int j = 0; j < nvars_ - naux_; ++j) { SCIP_VARTYPE vartype = SCIPvarGetType(vars_[j]); if (vartype == SCIP_VARTYPE_CONTINUOUS) continue; double viol = 0.5 - fabs(vals[j] - floor(vals[j]) - 0.5); if (viol > maxviol) maxviol = viol; } DSPdebugMessage("maximum violation %e\n", maxviol); if (where != from_scip_check && where != from_scip_enfolp && where != from_scip_enfops && maxviol > 1.e-7) { printf("where %d maxviol %e\n", where, maxviol); /** free memory */ SCIPfreeMemoryArray(scip, &vals); return SCIP_OKAY; } #endif #ifdef DSP_DEBUG2 double minvals = COIN_DBL_MAX; double maxvals = -COIN_DBL_MAX; double sumvals = 0.; double ssvals = 0.; //printf("nvars_ %d naux_ %d nAuxvars_ %d\n", nvars_, naux_, tss_->nAuxvars_); for (int j = 0; j < nvars_ - naux_; ++j) { // if (vals[j] < 0 || vals[j] > 1) // printf("solution %d has value %e.\n", j, vals[j]); sumvals += vals[j]; ssvals += vals[j] * vals[j]; minvals = minvals > vals[j] ? vals[j] : minvals; maxvals = maxvals < vals[j] ? vals[j] : maxvals; } DSPdebugMessage("solution: min %e max %e avg %e sum %e two-norm %e\n", minvals, maxvals, sumvals / nvars_, sumvals, sqrt(ssvals)); #endif #define SCAN_GLOBAL_CUT_POOL #ifdef SCAN_GLOBAL_CUT_POOL if (SCIPgetStage(scip) == SCIP_STAGE_SOLVING || SCIPgetStage(scip) == SCIP_STAGE_SOLVED || SCIPgetStage(scip) == SCIP_STAGE_EXITSOLVE) { bool addedPoolCut = false; int numPoolCuts = SCIPgetNPoolCuts(scip); int numCutsToScan = 100; SCIP_CUT ** poolcuts = SCIPgetPoolCuts(scip); for (int i = numPoolCuts - 1; i >= 0; --i) { if (i < 0) break; if (numCutsToScan == 0) break; /** retrieve row */ SCIP_ROW * poolcutrow = SCIPcutGetRow(poolcuts[i]); /** benders? */ if (strcmp(SCIProwGetName(poolcutrow), "benders") != 0) continue; /** counter */ numCutsToScan--; if (SCIPgetCutEfficacy(scip, sol, poolcutrow) > 1.e-6) { if (where == from_scip_sepalp || where == from_scip_sepasol || where == from_scip_enfolp) { /** add cut */ SCIP_Bool infeasible; SCIP_CALL(SCIPaddCut(scip, sol, poolcutrow, FALSE, /**< force cut */ &infeasible)); if (infeasible) *result = SCIP_CUTOFF; else //if (*result != SCIP_CUTOFF) *result = SCIP_SEPARATED; } else *result = SCIP_INFEASIBLE; addedPoolCut = true; break; } } if (addedPoolCut) { DSPdebugMessage("Added pool cut\n"); /** free memory */ SCIPfreeMemoryArray(scip, &vals); return SCIP_OKAY; } } #endif /** generate Benders cuts */ assert(tss_); tss_->generateCuts(nvars_, vals, &cs); /** If found Benders cuts */ for (int i = 0; i < cs.sizeCuts(); ++i) { /** get cut pointer */ OsiRowCut * rc = cs.rowCutPtr(i); if (!rc) continue; const CoinPackedVector cutrow = rc->row(); if (cutrow.getNumElements() == 0) continue; /** is optimality cut? */ bool isOptimalityCut = false; for (int j = nvars_ - naux_; j < nvars_; ++j) { if (cutrow.getMaxIndex() == j) { isOptimalityCut = true; break; } } double efficacy = rc->violated(vals) / cutrow.twoNorm(); SCIP_Bool isEfficacious = efficacy > 1.e-6; #define KK_TEST #ifdef KK_TEST if (SCIPgetStage(scip) == SCIP_STAGE_INITSOLVE || SCIPgetStage(scip) == SCIP_STAGE_SOLVING) { /** create empty row */ SCIP_ROW * row = NULL; SCIP_CALL(SCIPcreateEmptyRowCons(scip, &row, conshdlr, "benders", rc->lb(), SCIPinfinity(scip), FALSE, /**< is row local? */ FALSE, /**< is row modifiable? */ FALSE /**< is row removable? can this be TRUE? */)); /** cache the row extension and only flush them if the cut gets added */ SCIP_CALL(SCIPcacheRowExtensions(scip, row)); /** collect all non-zero coefficients */ for (int j = 0; j < cutrow.getNumElements(); ++j) SCIP_CALL(SCIPaddVarToRow(scip, row, vars_[cutrow.getIndices()[j]], cutrow.getElements()[j])); DSPdebugMessage("found Benders (%s) cut: act=%f, lhs=%f, norm=%f, eff=%f, min=%f, max=%f (range=%f)\n", isOptimalityCut ? "opti" : "feas", SCIPgetRowLPActivity(scip, row), SCIProwGetLhs(row), SCIProwGetNorm(row), SCIPgetCutEfficacy(scip, sol, row), SCIPgetRowMinCoef(scip, row), SCIPgetRowMaxCoef(scip, row), SCIPgetRowMaxCoef(scip, row)/SCIPgetRowMinCoef(scip, row)); /** flush all changes before adding cut */ SCIP_CALL(SCIPflushRowExtensions(scip, row)); DSPdebugMessage("efficacy %e isEfficatious %d\n", efficacy, isEfficacious); if (isEfficacious) { if (where == from_scip_sepalp || where == from_scip_sepasol || where == from_scip_enfolp) { /** add cut */ SCIP_Bool infeasible; SCIP_CALL(SCIPaddCut(scip, sol, row, FALSE, /**< force cut */ &infeasible)); if (infeasible) *result = SCIP_CUTOFF; else //if (*result != SCIP_CUTOFF) *result = SCIP_SEPARATED; } else *result = SCIP_INFEASIBLE; } /** add cut to global pool */ SCIP_CALL(SCIPaddPoolCut(scip, row)); DSPdebugMessage("number of cuts in global cut pool: %d\n", SCIPgetNPoolCuts(scip)); /** release the row */ SCIP_CALL(SCIPreleaseRow(scip, &row)); } else if (isEfficacious && where != from_scip_sepalp && where != from_scip_sepasol && where != from_scip_enfolp) *result = SCIP_INFEASIBLE; #else if (where == from_scip_sepalp || where == from_scip_sepasol || where == from_scip_enfolp) { /** create empty row */ SCIP_ROW * row = NULL; SCIP_CALL(SCIPcreateEmptyRowCons(scip, &row, conshdlr, "benders", rc->lb(), SCIPinfinity(scip), FALSE, /**< is row local? */ FALSE, /**< is row modifiable? */ FALSE /**< is row removable? can this be TRUE? */)); /** cache the row extension and only flush them if the cut gets added */ SCIP_CALL(SCIPcacheRowExtensions(scip, row)); /** collect all non-zero coefficients */ for (int j = 0; j < cutrow.getNumElements(); ++j) SCIP_CALL(SCIPaddVarToRow(scip, row, vars_[cutrow.getIndices()[j]], cutrow.getElements()[j])); DSPdebugMessage("found Benders (%s) cut: act=%f, lhs=%f, norm=%f, eff=%f, min=%f, max=%f (range=%f)\n", isOptimalityCut ? "opti" : "feas", SCIPgetRowLPActivity(scip, row), SCIProwGetLhs(row), SCIProwGetNorm(row), SCIPgetCutEfficacy(scip, NULL, row), SCIPgetRowMinCoef(scip, row), SCIPgetRowMaxCoef(scip, row), SCIPgetRowMaxCoef(scip, row)/SCIPgetRowMinCoef(scip, row)); /** flush all changes before adding cut */ SCIP_CALL(SCIPflushRowExtensions(scip, row)); /** is cut efficacious? */ if (isOptimalityCut) { efficacy = SCIPgetCutEfficacy(scip, sol, row); isEfficacious = SCIPisCutEfficacious(scip, sol, row); } else { efficacy = rc->violated(vals); isEfficacious = efficacy > 1.e-6; } if (isEfficacious) { /** add cut */ SCIP_Bool infeasible; SCIP_CALL(SCIPaddCut(scip, sol, row, FALSE, /**< force cut */ &infeasible)); if (infeasible) *result = SCIP_CUTOFF; else if (*result != SCIP_CUTOFF) *result = SCIP_SEPARATED; } /** add cut to global pool */ SCIP_CALL(SCIPaddPoolCut(scip, row)); /** release the row */ SCIP_CALL(SCIPreleaseRow(scip, &row)); } else { if (isOptimalityCut) { efficacy = rc->violated(vals) / cutrow.twoNorm(); isEfficacious = efficacy > 0.05; } else { efficacy = rc->violated(vals); isEfficacious = efficacy > 1.e-6; } DSPdebugMessage("%s efficacy %e\n", isOptimalityCut ? "Opti" : "Feas", efficacy); if (isEfficacious == TRUE) *result = SCIP_INFEASIBLE; } #endif } /** free memory */ SCIPfreeMemoryArray(scip, &vals); return SCIP_OKAY; }