/** execution method of primal heuristic */ static SCIP_DECL_HEUREXEC(heurExecIntdiving) /*lint --e{715}*/ { /*lint --e{715}*/ SCIP_HEURDATA* heurdata; SCIP_LPSOLSTAT lpsolstat; SCIP_VAR** pseudocands; SCIP_VAR** fixcands; SCIP_Real* fixcandscores; SCIP_Real searchubbound; SCIP_Real searchavgbound; SCIP_Real searchbound; SCIP_Real objval; SCIP_Bool lperror; SCIP_Bool cutoff; SCIP_Bool backtracked; SCIP_Longint ncalls; SCIP_Longint nsolsfound; SCIP_Longint nlpiterations; SCIP_Longint maxnlpiterations; int nfixcands; int nbinfixcands; int depth; int maxdepth; int maxdivedepth; int divedepth; int nextcand; int c; assert(heur != NULL); assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0); assert(scip != NULL); assert(result != NULL); assert(SCIPhasCurrentNodeLP(scip)); *result = SCIP_DELAYED; /* do not call heuristic of node was already detected to be infeasible */ if( nodeinfeasible ) return SCIP_OKAY; /* only call heuristic, if an optimal LP solution is at hand */ if( SCIPgetLPSolstat(scip) != SCIP_LPSOLSTAT_OPTIMAL ) return SCIP_OKAY; /* only call heuristic, if the LP objective value is smaller than the cutoff bound */ if( SCIPisGE(scip, SCIPgetLPObjval(scip), SCIPgetCutoffbound(scip)) ) return SCIP_OKAY; /* only call heuristic, if the LP solution is basic (which allows fast resolve in diving) */ if( !SCIPisLPSolBasic(scip) ) return SCIP_OKAY; /* don't dive two times at the same node */ if( SCIPgetLastDivenode(scip) == SCIPgetNNodes(scip) && SCIPgetDepth(scip) > 0 ) return SCIP_OKAY; *result = SCIP_DIDNOTRUN; /* get heuristic's data */ heurdata = SCIPheurGetData(heur); assert(heurdata != NULL); /* only try to dive, if we are in the correct part of the tree, given by minreldepth and maxreldepth */ depth = SCIPgetDepth(scip); maxdepth = SCIPgetMaxDepth(scip); maxdepth = MAX(maxdepth, 100); if( depth < heurdata->minreldepth*maxdepth || depth > heurdata->maxreldepth*maxdepth ) return SCIP_OKAY; /* calculate the maximal number of LP iterations until heuristic is aborted */ nlpiterations = SCIPgetNNodeLPIterations(scip); ncalls = SCIPheurGetNCalls(heur); nsolsfound = 10*SCIPheurGetNBestSolsFound(heur) + heurdata->nsuccess; maxnlpiterations = (SCIP_Longint)((1.0 + 10.0*(nsolsfound+1.0)/(ncalls+1.0)) * heurdata->maxlpiterquot * nlpiterations); maxnlpiterations += heurdata->maxlpiterofs; /* don't try to dive, if we took too many LP iterations during diving */ if( heurdata->nlpiterations >= maxnlpiterations ) return SCIP_OKAY; /* allow at least a certain number of LP iterations in this dive */ maxnlpiterations = MAX(maxnlpiterations, heurdata->nlpiterations + MINLPITER); /* get unfixed integer variables */ SCIP_CALL( SCIPgetPseudoBranchCands(scip, &pseudocands, &nfixcands, NULL) ); /* don't try to dive, if there are no fractional variables */ if( nfixcands == 0 ) return SCIP_OKAY; /* calculate the objective search bound */ if( SCIPgetNSolsFound(scip) == 0 ) { if( heurdata->maxdiveubquotnosol > 0.0 ) searchubbound = SCIPgetLowerbound(scip) + heurdata->maxdiveubquotnosol * (SCIPgetCutoffbound(scip) - SCIPgetLowerbound(scip)); else searchubbound = SCIPinfinity(scip); if( heurdata->maxdiveavgquotnosol > 0.0 ) searchavgbound = SCIPgetLowerbound(scip) + heurdata->maxdiveavgquotnosol * (SCIPgetAvgLowerbound(scip) - SCIPgetLowerbound(scip)); else searchavgbound = SCIPinfinity(scip); } else { if( heurdata->maxdiveubquot > 0.0 ) searchubbound = SCIPgetLowerbound(scip) + heurdata->maxdiveubquot * (SCIPgetCutoffbound(scip) - SCIPgetLowerbound(scip)); else searchubbound = SCIPinfinity(scip); if( heurdata->maxdiveavgquot > 0.0 ) searchavgbound = SCIPgetLowerbound(scip) + heurdata->maxdiveavgquot * (SCIPgetAvgLowerbound(scip) - SCIPgetLowerbound(scip)); else searchavgbound = SCIPinfinity(scip); } searchbound = MIN(searchubbound, searchavgbound); if( SCIPisObjIntegral(scip) ) searchbound = SCIPceil(scip, searchbound); /* calculate the maximal diving depth: 10 * min{number of integer variables, max depth} */ maxdivedepth = SCIPgetNBinVars(scip) + SCIPgetNIntVars(scip); maxdivedepth = MIN(maxdivedepth, maxdepth); maxdivedepth *= 10; *result = SCIP_DIDNOTFIND; /* start diving */ SCIP_CALL( SCIPstartProbing(scip) ); /* enables collection of variable statistics during probing */ SCIPenableVarHistory(scip); SCIPdebugMessage("(node %" SCIP_LONGINT_FORMAT ") executing intdiving heuristic: depth=%d, %d non-fixed, dualbound=%g, searchbound=%g\n", SCIPgetNNodes(scip), SCIPgetDepth(scip), nfixcands, SCIPgetDualbound(scip), SCIPretransformObj(scip, searchbound)); /* copy the pseudo candidates into own array, because we want to reorder them */ SCIP_CALL( SCIPduplicateBufferArray(scip, &fixcands, pseudocands, nfixcands) ); /* sort non-fixed variables by non-increasing inference score, but prefer binaries over integers in any case */ SCIP_CALL( SCIPallocBufferArray(scip, &fixcandscores, nfixcands) ); nbinfixcands = 0; for( c = 0; c < nfixcands; ++c ) { SCIP_VAR* var; SCIP_Real score; int colveclen; int left; int right; int i; assert(c >= nbinfixcands); var = fixcands[c]; assert(SCIPvarIsIntegral(var)); colveclen = (SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN ? SCIPcolGetNNonz(SCIPvarGetCol(var)) : 0); if( SCIPvarIsBinary(var) ) { score = 500.0 * SCIPvarGetNCliques(var, TRUE) + 100.0 * SCIPvarGetNImpls(var, TRUE) + SCIPgetVarAvgInferenceScore(scip, var) + (SCIP_Real)colveclen/100.0; /* shift the non-binary variables one slot to the right */ for( i = c; i > nbinfixcands; --i ) { fixcands[i] = fixcands[i-1]; fixcandscores[i] = fixcandscores[i-1]; } /* put the new candidate into the first nbinfixcands slot */ left = 0; right = nbinfixcands; nbinfixcands++; } else { score = 5.0 * (SCIPvarGetNCliques(var, FALSE) + SCIPvarGetNCliques(var, TRUE)) + SCIPvarGetNImpls(var, FALSE) + SCIPvarGetNImpls(var, TRUE) + SCIPgetVarAvgInferenceScore(scip, var) + (SCIP_Real)colveclen/10000.0; /* put the new candidate in the slots after the binary candidates */ left = nbinfixcands; right = c; } for( i = right; i > left && score > fixcandscores[i-1]; --i ) { fixcands[i] = fixcands[i-1]; fixcandscores[i] = fixcandscores[i-1]; } fixcands[i] = var; fixcandscores[i] = score; SCIPdebugMessage(" <%s>: ncliques=%d/%d, nimpls=%d/%d, inferencescore=%g, colveclen=%d -> score=%g\n", SCIPvarGetName(var), SCIPvarGetNCliques(var, FALSE), SCIPvarGetNCliques(var, TRUE), SCIPvarGetNImpls(var, FALSE), SCIPvarGetNImpls(var, TRUE), SCIPgetVarAvgInferenceScore(scip, var), colveclen, score); } SCIPfreeBufferArray(scip, &fixcandscores); /* get LP objective value */ lpsolstat = SCIP_LPSOLSTAT_OPTIMAL; objval = SCIPgetLPObjval(scip); /* dive as long we are in the given objective, depth and iteration limits, but if possible, we dive at least with * the depth 10 */ lperror = FALSE; cutoff = FALSE; divedepth = 0; nextcand = 0; while( !lperror && !cutoff && lpsolstat == SCIP_LPSOLSTAT_OPTIMAL && (divedepth < 10 || (divedepth < maxdivedepth && heurdata->nlpiterations < maxnlpiterations && objval < searchbound)) && !SCIPisStopped(scip) ) { SCIP_VAR* var; SCIP_Real bestsolval; SCIP_Real bestfixval; int bestcand; SCIP_Longint nnewlpiterations; SCIP_Longint nnewdomreds; /* open a new probing node if this will not exceed the maximal tree depth, otherwise stop here */ if( SCIPgetDepth(scip) < SCIPgetDepthLimit(scip) ) { SCIP_CALL( SCIPnewProbingNode(scip) ); divedepth++; } else break; nnewlpiterations = 0; nnewdomreds = 0; /* fix binary variable that is closest to 1 in the LP solution to 1; * if all binary variables are fixed, fix integer variable with least fractionality in LP solution */ bestcand = -1; bestsolval = -1.0; bestfixval = 1.0; /* look in the binary variables for fixing candidates */ for( c = nextcand; c < nbinfixcands; ++c ) { SCIP_Real solval; var = fixcands[c]; /* ignore already fixed variables */ if( var == NULL ) continue; if( SCIPvarGetLbLocal(var) > 0.5 || SCIPvarGetUbLocal(var) < 0.5 ) { fixcands[c] = NULL; continue; } /* get the LP solution value */ solval = SCIPvarGetLPSol(var); if( solval > bestsolval ) { bestcand = c; bestfixval = 1.0; bestsolval = solval; if( SCIPisGE(scip, bestsolval, 1.0) ) { /* we found an unfixed binary variable with LP solution value of 1.0 - there cannot be a better candidate */ break; } else if( SCIPisLE(scip, bestsolval, 0.0) ) { /* the variable is currently at 0.0 - this is the only situation where we want to fix it to 0.0 */ bestfixval = 0.0; } } } /* if all binary variables are fixed, look in the integer variables for a fixing candidate */ if( bestcand == -1 ) { SCIP_Real bestfrac; bestfrac = SCIP_INVALID; for( c = MAX(nextcand, nbinfixcands); c < nfixcands; ++c ) { SCIP_Real solval; SCIP_Real frac; var = fixcands[c]; /* ignore already fixed variables */ if( var == NULL ) continue; if( SCIPvarGetUbLocal(var) - SCIPvarGetLbLocal(var) < 0.5 ) { fixcands[c] = NULL; continue; } /* get the LP solution value */ solval = SCIPvarGetLPSol(var); frac = SCIPfrac(scip, solval); /* ignore integer variables that are currently integral */ if( SCIPisFeasFracIntegral(scip, frac) ) continue; if( frac < bestfrac ) { bestcand = c; bestsolval = solval; bestfrac = frac; bestfixval = SCIPfloor(scip, bestsolval + 0.5); if( SCIPisZero(scip, bestfrac) ) { /* we found an unfixed integer variable with integral LP solution value */ break; } } } } assert(-1 <= bestcand && bestcand < nfixcands); /* if there is no unfixed candidate left, we are done */ if( bestcand == -1 ) break; var = fixcands[bestcand]; assert(var != NULL); assert(SCIPvarIsIntegral(var)); assert(SCIPvarGetUbLocal(var) - SCIPvarGetLbLocal(var) > 0.5); assert(SCIPisGE(scip, bestfixval, SCIPvarGetLbLocal(var))); assert(SCIPisLE(scip, bestfixval, SCIPvarGetUbLocal(var))); backtracked = FALSE; do { /* if the variable is already fixed or if the solution value is outside the domain, numerical troubles may have * occured or variable was fixed by propagation while backtracking => Abort diving! */ if( SCIPvarGetLbLocal(var) >= SCIPvarGetUbLocal(var) - 0.5 ) { SCIPdebugMessage("Selected variable <%s> already fixed to [%g,%g], diving aborted \n", SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)); cutoff = TRUE; break; } if( SCIPisFeasLT(scip, bestfixval, SCIPvarGetLbLocal(var)) || SCIPisFeasGT(scip, bestfixval, SCIPvarGetUbLocal(var)) ) { SCIPdebugMessage("selected variable's <%s> solution value is outside the domain [%g,%g] (solval: %.9f), diving aborted\n", SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var), bestfixval); assert(backtracked); break; } /* apply fixing of best candidate */ SCIPdebugMessage(" dive %d/%d, LP iter %" SCIP_LONGINT_FORMAT "/%" SCIP_LONGINT_FORMAT ", %d unfixed: var <%s>, sol=%g, oldbounds=[%g,%g], fixed to %g\n", divedepth, maxdivedepth, heurdata->nlpiterations, maxnlpiterations, SCIPgetNPseudoBranchCands(scip), SCIPvarGetName(var), bestsolval, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var), bestfixval); SCIP_CALL( SCIPfixVarProbing(scip, var, bestfixval) ); /* apply domain propagation */ SCIP_CALL( SCIPpropagateProbing(scip, 0, &cutoff, &nnewdomreds) ); if( !cutoff ) { /* if the best candidate was just fixed to its LP value and no domain reduction was found, the LP solution * stays valid, and the LP does not need to be resolved */ if( nnewdomreds > 0 || !SCIPisEQ(scip, bestsolval, bestfixval) ) { /* resolve the diving LP */ /* Errors in the LP solver should not kill the overall solving process, if the LP is just needed for a heuristic. * Hence in optimized mode, the return code is caught and a warning is printed, only in debug mode, SCIP will stop. */ #ifdef NDEBUG SCIP_RETCODE retstat; nlpiterations = SCIPgetNLPIterations(scip); retstat = SCIPsolveProbingLP(scip, MAX((int)(maxnlpiterations - heurdata->nlpiterations), MINLPITER), &lperror, &cutoff); if( retstat != SCIP_OKAY ) { SCIPwarningMessage(scip, "Error while solving LP in Intdiving heuristic; LP solve terminated with code <%d>\n",retstat); } #else nlpiterations = SCIPgetNLPIterations(scip); SCIP_CALL( SCIPsolveProbingLP(scip, MAX((int)(maxnlpiterations - heurdata->nlpiterations), MINLPITER), &lperror, &cutoff) ); #endif if( lperror ) break; /* update iteration count */ nnewlpiterations = SCIPgetNLPIterations(scip) - nlpiterations; heurdata->nlpiterations += nnewlpiterations; /* get LP solution status */ lpsolstat = SCIPgetLPSolstat(scip); assert(cutoff || (lpsolstat != SCIP_LPSOLSTAT_OBJLIMIT && lpsolstat != SCIP_LPSOLSTAT_INFEASIBLE && (lpsolstat != SCIP_LPSOLSTAT_OPTIMAL || SCIPisLT(scip, SCIPgetLPObjval(scip), SCIPgetCutoffbound(scip))))); } } /* perform backtracking if a cutoff was detected */ if( cutoff && !backtracked && heurdata->backtrack ) { SCIPdebugMessage(" *** cutoff detected at level %d - backtracking\n", SCIPgetProbingDepth(scip)); SCIP_CALL( SCIPbacktrackProbing(scip, SCIPgetProbingDepth(scip)-1) ); /* after backtracking there has to be at least one open node without exceeding the maximal tree depth */ assert(SCIPgetDepthLimit(scip) > SCIPgetDepth(scip)); SCIP_CALL( SCIPnewProbingNode(scip) ); bestfixval = SCIPvarIsBinary(var) ? 1.0 - bestfixval : (SCIPisGT(scip, bestsolval, bestfixval) && SCIPisFeasLE(scip, bestfixval + 1, SCIPvarGetUbLocal(var)) ? bestfixval + 1 : bestfixval - 1); backtracked = TRUE; } else backtracked = FALSE; } while( backtracked ); if( !lperror && !cutoff && lpsolstat == SCIP_LPSOLSTAT_OPTIMAL ) { SCIP_Bool success; /* get new objective value */ objval = SCIPgetLPObjval(scip); if( nnewlpiterations > 0 || !SCIPisEQ(scip, bestsolval, bestfixval) ) { /* we must start again with the first candidate, since the LP solution changed */ nextcand = 0; /* create solution from diving LP and try to round it */ SCIP_CALL( SCIPlinkLPSol(scip, heurdata->sol) ); SCIP_CALL( SCIProundSol(scip, heurdata->sol, &success) ); if( success ) { SCIPdebugMessage("intdiving found roundable primal solution: obj=%g\n", SCIPgetSolOrigObj(scip, heurdata->sol)); /* try to add solution to SCIP */ SCIP_CALL( SCIPtrySol(scip, heurdata->sol, FALSE, FALSE, FALSE, FALSE, &success) ); /* check, if solution was feasible and good enough */ if( success ) { SCIPdebugMessage(" -> solution was feasible and good enough\n"); *result = SCIP_FOUNDSOL; } } } else nextcand = bestcand+1; /* continue with the next candidate in the following loop */ } SCIPdebugMessage(" -> lpsolstat=%d, objval=%g/%g\n", lpsolstat, objval, searchbound); } /* free temporary memory */ SCIPfreeBufferArray(scip, &fixcands); /* end diving */ SCIP_CALL( SCIPendProbing(scip) ); if( *result == SCIP_FOUNDSOL ) heurdata->nsuccess++; SCIPdebugMessage("intdiving heuristic finished\n"); return SCIP_OKAY; }
/** reduced cost propagation method for an LP solution */ static SCIP_DECL_PROPEXEC(propExecRedcost) { /*lint --e{715}*/ SCIP_PROPDATA* propdata; SCIP_COL** cols; SCIP_Real requiredredcost; SCIP_Real cutoffbound; SCIP_Real lpobjval; SCIP_Bool propbinvars; SCIP_Bool cutoff; int nchgbds; int ncols; int c; *result = SCIP_DIDNOTRUN; /* in case we have a zero objective function, we skip the reduced cost propagator */ if( SCIPgetNObjVars(scip) == 0 ) return SCIP_OKAY; /* propagator can only be applied during solving stage */ if( SCIPgetStage(scip) < SCIP_STAGE_SOLVING ) return SCIP_OKAY; /* we cannot apply reduced cost fixing, if we want to solve exactly */ /**@todo implement reduced cost fixing with interval arithmetics */ if( SCIPisExactSolve(scip) ) return SCIP_OKAY; /* only call propagator, if the current node has an LP */ if( !SCIPhasCurrentNodeLP(scip) ) return SCIP_OKAY; /* only call propagator, if an optimal LP solution is at hand */ if( SCIPgetLPSolstat(scip) != SCIP_LPSOLSTAT_OPTIMAL ) return SCIP_OKAY; /* only call propagator, if the current LP is a valid relaxation */ if( !SCIPisLPRelax(scip) ) return SCIP_OKAY; /* we cannot apply reduced cost strengthening, if no simplex basis is available */ if( !SCIPisLPSolBasic(scip) ) return SCIP_OKAY; /* get current cutoff bound */ cutoffbound = SCIPgetCutoffbound(scip); /* reduced cost strengthening can only be applied, if we have a finite cutoff */ if( SCIPisInfinity(scip, cutoffbound) ) return SCIP_OKAY; /* get LP columns */ cols = SCIPgetLPCols(scip); ncols = SCIPgetNLPCols(scip); /* do nothing if the LP has no columns (is empty) */ if( ncols == 0 ) return SCIP_OKAY; /* get propagator data */ propdata = SCIPpropGetData(prop); assert(propdata != NULL); /* chack if all integral variables are fixed and the continuous variables should not be propagated */ if( !propdata->continuous && SCIPgetNPseudoBranchCands(scip) == 0 ) return SCIP_OKAY; /* get LP objective value */ lpobjval = SCIPgetLPObjval(scip); /* check if binary variables should be propagated */ propbinvars = (SCIPgetDepth(scip) == 0) || (cutoffbound - lpobjval < 5 * propdata->maxredcost); /* skip the propagator if the problem has only binary variables and those should not be propagated */ if( !propbinvars && SCIPgetNVars(scip) == SCIPgetNBinVars(scip) ) return SCIP_OKAY; *result = SCIP_DIDNOTFIND; cutoff = FALSE; nchgbds = 0; /* compute the required reduced cost which are needed for a binary variable to be fixed */ requiredredcost = cutoffbound - lpobjval; SCIPdebugMessage("lpobjval <%g>, cutoffbound <%g>, max reduced <%g>, propgate binary %u, use implics %u\n", lpobjval, cutoffbound, propdata->maxredcost, propbinvars, propdata->usefullimplics); /* check reduced costs for non-basic columns */ for( c = 0; c < ncols && !cutoff; ++c ) { SCIP_VAR* var; var = SCIPcolGetVar(cols[c]); /* skip continuous variables in case the corresponding parameter is set */ if( !propdata->continuous && !SCIPvarIsIntegral(var) ) continue; if( SCIPvarIsBinary(var) ) { if( propbinvars ) { if( SCIPgetDepth(scip) == 0 ) { SCIP_CALL( propagateRootRedcostBinvar(scip, propdata, var, cols[c], cutoffbound, &nchgbds) ); } else { SCIP_CALL( propagateRedcostBinvar(scip, propdata, var, cols[c], requiredredcost, &nchgbds, &cutoff) ); } } } else { SCIP_CALL( propagateRedcostVar(scip, var, cols[c], lpobjval, cutoffbound, &nchgbds) ); } } if( cutoff ) { *result = SCIP_CUTOFF; SCIPdebugMessage("node %"SCIP_LONGINT_FORMAT": detected cutoff\n", SCIPnodeGetNumber(SCIPgetCurrentNode(scip))); } else if( nchgbds > 0 ) { *result = SCIP_REDUCEDDOM; SCIPdebugMessage("node %"SCIP_LONGINT_FORMAT": %d bound changes (max redcost <%g>)\n", SCIPnodeGetNumber(SCIPgetCurrentNode(scip)) , nchgbds, propdata->maxredcost); } return SCIP_OKAY; }
/** LP solution separation method of separator */ static SCIP_DECL_SEPAEXECLP(sepaExeclpStrongcg) { /*lint --e{715}*/ SCIP_SEPADATA* sepadata; SCIP_VAR** vars; SCIP_COL** cols; SCIP_ROW** rows; SCIP_Real* varsolvals; SCIP_Real* binvrow; SCIP_Real* cutcoefs; SCIP_Real cutrhs; SCIP_Real cutact; SCIP_Real maxscale; SCIP_Longint maxdnom; int* basisind; int* inds; int ninds; int nvars; int ncols; int nrows; int ncalls; int depth; int maxdepth; int maxsepacuts; int ncuts; int c; int i; int cutrank; SCIP_Bool success; SCIP_Bool cutislocal; char normtype; assert(sepa != NULL); assert(strcmp(SCIPsepaGetName(sepa), SEPA_NAME) == 0); assert(scip != NULL); assert(result != NULL); *result = SCIP_DIDNOTRUN; sepadata = SCIPsepaGetData(sepa); assert(sepadata != NULL); depth = SCIPgetDepth(scip); ncalls = SCIPsepaGetNCallsAtNode(sepa); /* only call separator, if we are not close to terminating */ if( SCIPisStopped(scip) ) return SCIP_OKAY; /* only call the strong CG cut separator a given number of times at each node */ if( (depth == 0 && sepadata->maxroundsroot >= 0 && ncalls >= sepadata->maxroundsroot) || (depth > 0 && sepadata->maxrounds >= 0 && ncalls >= sepadata->maxrounds) ) return SCIP_OKAY; /* only call separator, if an optimal LP solution is at hand */ if( SCIPgetLPSolstat(scip) != SCIP_LPSOLSTAT_OPTIMAL ) return SCIP_OKAY; /* only call separator, if the LP solution is basic */ if( !SCIPisLPSolBasic(scip) ) return SCIP_OKAY; /* only call separator, if there are fractional variables */ if( SCIPgetNLPBranchCands(scip) == 0 ) return SCIP_OKAY; /* get variables data */ SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL) ); /* get LP data */ SCIP_CALL( SCIPgetLPColsData(scip, &cols, &ncols) ); SCIP_CALL( SCIPgetLPRowsData(scip, &rows, &nrows) ); if( ncols == 0 || nrows == 0 ) return SCIP_OKAY; #if 0 /* if too many columns, separator is usually very slow: delay it until no other cuts have been found */ if( ncols >= 50*nrows ) return SCIP_OKAY; if( ncols >= 5*nrows ) { int ncutsfound; ncutsfound = SCIPgetNCutsFound(scip); if( ncutsfound > sepadata->lastncutsfound || !SCIPsepaWasLPDelayed(sepa) ) { sepadata->lastncutsfound = ncutsfound; *result = SCIP_DELAYED; return SCIP_OKAY; } } #endif /* get the type of norm to use for efficacy calculations */ SCIP_CALL( SCIPgetCharParam(scip, "separating/efficacynorm", &normtype) ); /* set the maximal denominator in rational representation of strong CG cut and the maximal scale factor to * scale resulting cut to integral values to avoid numerical instabilities */ /**@todo find better but still stable strong CG cut settings: look at dcmulti, gesa3, khb0525, misc06, p2756 */ maxdepth = SCIPgetMaxDepth(scip); if( depth == 0 ) { maxdnom = 1000; maxscale = 1000.0; } else if( depth <= maxdepth/4 ) { maxdnom = 1000; maxscale = 1000.0; } else if( depth <= maxdepth/2 ) { maxdnom = 100; maxscale = 100.0; } else { maxdnom = 10; maxscale = 10.0; } *result = SCIP_DIDNOTFIND; /* allocate temporary memory */ SCIP_CALL( SCIPallocBufferArray(scip, &cutcoefs, nvars) ); SCIP_CALL( SCIPallocBufferArray(scip, &basisind, nrows) ); SCIP_CALL( SCIPallocBufferArray(scip, &binvrow, nrows) ); SCIP_CALL( SCIPallocBufferArray(scip, &inds, nrows) ); varsolvals = NULL; /* allocate this later, if needed */ /* get basis indices */ SCIP_CALL( SCIPgetLPBasisInd(scip, basisind) ); /* get the maximal number of cuts allowed in a separation round */ if( depth == 0 ) maxsepacuts = sepadata->maxsepacutsroot; else maxsepacuts = sepadata->maxsepacuts; SCIPdebugMessage("searching strong CG cuts: %d cols, %d rows, maxdnom=%" SCIP_LONGINT_FORMAT ", maxscale=%g, maxcuts=%d\n", ncols, nrows, maxdnom, maxscale, maxsepacuts); /* for all basic columns belonging to integer variables, try to generate a strong CG cut */ ncuts = 0; for( i = 0; i < nrows && ncuts < maxsepacuts && !SCIPisStopped(scip) && *result != SCIP_CUTOFF; ++i ) { SCIP_Bool tryrow; tryrow = FALSE; c = basisind[i]; if( c >= 0 ) { SCIP_VAR* var; assert(c < ncols); var = SCIPcolGetVar(cols[c]); if( SCIPvarGetType(var) != SCIP_VARTYPE_CONTINUOUS ) { SCIP_Real primsol; primsol = SCIPcolGetPrimsol(cols[c]); assert(SCIPgetVarSol(scip, var) == primsol); /*lint !e777*/ if( SCIPfeasFrac(scip, primsol) >= MINFRAC ) { SCIPdebugMessage("trying strong CG cut for col <%s> [%g]\n", SCIPvarGetName(var), primsol); tryrow = TRUE; } } } #ifdef SEPARATEROWS else { SCIP_ROW* row; assert(0 <= -c-1 && -c-1 < nrows); row = rows[-c-1]; if( SCIProwIsIntegral(row) && !SCIProwIsModifiable(row) ) { SCIP_Real primsol; primsol = SCIPgetRowActivity(scip, row); if( SCIPfeasFrac(scip, primsol) >= MINFRAC ) { SCIPdebugMessage("trying strong CG cut for row <%s> [%g]\n", SCIProwGetName(row), primsol); tryrow = TRUE; } } } #endif if( tryrow ) { /* get the row of B^-1 for this basic integer variable with fractional solution value */ SCIP_CALL( SCIPgetLPBInvRow(scip, i, binvrow, inds, &ninds) ); #ifdef SCIP_DEBUG /* initialize variables, that might not have been initialized in SCIPcalcMIR if success == FALSE */ cutact = 0.0; cutrhs = SCIPinfinity(scip); #endif /* create a strong CG cut out of the weighted LP rows using the B^-1 row as weights */ SCIP_CALL( SCIPcalcStrongCG(scip, BOUNDSWITCH, USEVBDS, ALLOWLOCAL, (int) MAXAGGRLEN(nvars), sepadata->maxweightrange, MINFRAC, MAXFRAC, binvrow, inds, ninds, 1.0, cutcoefs, &cutrhs, &cutact, &success, &cutislocal, &cutrank) ); assert(ALLOWLOCAL || !cutislocal); SCIPdebugMessage(" -> success=%u: %g <= %g\n", success, cutact, cutrhs); /* if successful, convert dense cut into sparse row, and add the row as a cut */ if( success && SCIPisFeasGT(scip, cutact, cutrhs) ) { SCIP_VAR** cutvars; SCIP_Real* cutvals; SCIP_Real cutnorm; int cutlen; /* if this is the first successful cut, get the LP solution for all COLUMN variables */ if( varsolvals == NULL ) { int v; SCIP_CALL( SCIPallocBufferArray(scip, &varsolvals, nvars) ); for( v = 0; v < nvars; ++v ) { if( SCIPvarGetStatus(vars[v]) == SCIP_VARSTATUS_COLUMN ) varsolvals[v] = SCIPvarGetLPSol(vars[v]); } } assert(varsolvals != NULL); /* get temporary memory for storing the cut as sparse row */ SCIP_CALL( SCIPallocBufferArray(scip, &cutvars, nvars) ); SCIP_CALL( SCIPallocBufferArray(scip, &cutvals, nvars) ); /* store the cut as sparse row, calculate activity and norm of cut */ SCIP_CALL( storeCutInArrays(scip, nvars, vars, cutcoefs, varsolvals, normtype, cutvars, cutvals, &cutlen, &cutact, &cutnorm) ); SCIPdebugMessage(" -> strong CG cut for <%s>: act=%f, rhs=%f, norm=%f, eff=%f, rank=%d\n", c >= 0 ? SCIPvarGetName(SCIPcolGetVar(cols[c])) : SCIProwGetName(rows[-c-1]), cutact, cutrhs, cutnorm, (cutact - cutrhs)/cutnorm, cutrank); if( SCIPisPositive(scip, cutnorm) && SCIPisEfficacious(scip, (cutact - cutrhs)/cutnorm) ) { SCIP_ROW* cut; char cutname[SCIP_MAXSTRLEN]; /* create the cut */ if( c >= 0 ) (void) SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "scg%d_x%d", SCIPgetNLPs(scip), c); else (void) SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "scg%d_s%d", SCIPgetNLPs(scip), -c-1); SCIP_CALL( SCIPcreateEmptyRowSepa(scip, &cut, sepa, cutname, -SCIPinfinity(scip), cutrhs, cutislocal, FALSE, sepadata->dynamiccuts) ); SCIP_CALL( SCIPaddVarsToRow(scip, cut, cutlen, cutvars, cutvals) ); /*SCIPdebug( SCIP_CALL(SCIPprintRow(scip, cut, NULL)) );*/ SCIProwChgRank(cut, cutrank); assert(success); #ifdef MAKECUTINTEGRAL /* try to scale the cut to integral values */ SCIP_CALL( SCIPmakeRowIntegral(scip, cut, -SCIPepsilon(scip), SCIPsumepsilon(scip), maxdnom, maxscale, MAKECONTINTEGRAL, &success) ); #else #ifdef MAKEINTCUTINTEGRAL /* try to scale the cut to integral values if there are no continuous variables * -> leads to an integral slack variable that can later be used for other cuts */ { int k = 0; while ( k < cutlen && SCIPvarIsIntegral(cutvars[k]) ) ++k; if( k == cutlen ) { SCIP_CALL( SCIPmakeRowIntegral(scip, cut, -SCIPepsilon(scip), SCIPsumepsilon(scip), maxdnom, maxscale, MAKECONTINTEGRAL, &success) ); } } #endif #endif #ifndef FORCECUTINTEGRAL success = TRUE; #endif if( success ) { if( !SCIPisCutEfficacious(scip, NULL, cut) ) { SCIPdebugMessage(" -> strong CG cut <%s> no longer efficacious: act=%f, rhs=%f, norm=%f, eff=%f\n", cutname, SCIPgetRowLPActivity(scip, cut), SCIProwGetRhs(cut), SCIProwGetNorm(cut), SCIPgetCutEfficacy(scip, NULL, cut)); /*SCIPdebug( SCIP_CALL(SCIPprintRow(scip, cut, NULL)) );*/ success = FALSE; } else { SCIP_Bool infeasible; SCIPdebugMessage(" -> found strong CG cut <%s>: act=%f, rhs=%f, norm=%f, eff=%f, min=%f, max=%f (range=%f)\n", cutname, SCIPgetRowLPActivity(scip, cut), SCIProwGetRhs(cut), SCIProwGetNorm(cut), SCIPgetCutEfficacy(scip, NULL, cut), SCIPgetRowMinCoef(scip, cut), SCIPgetRowMaxCoef(scip, cut), SCIPgetRowMaxCoef(scip, cut)/SCIPgetRowMinCoef(scip, cut)); /*SCIPdebug( SCIP_CALL(SCIPprintRow(scip, cut, NULL)) );*/ SCIP_CALL( SCIPaddCut(scip, NULL, cut, FALSE, &infeasible) ); if ( infeasible ) *result = SCIP_CUTOFF; else { if( !cutislocal ) { SCIP_CALL( SCIPaddPoolCut(scip, cut) ); } *result = SCIP_SEPARATED; } ncuts++; } } else { SCIPdebugMessage(" -> strong CG cut <%s> couldn't be scaled to integral coefficients: act=%f, rhs=%f, norm=%f, eff=%f\n", cutname, cutact, cutrhs, cutnorm, SCIPgetCutEfficacy(scip, NULL, cut)); } /* release the row */ SCIP_CALL( SCIPreleaseRow(scip, &cut) ); } /* free temporary memory */ SCIPfreeBufferArray(scip, &cutvals); SCIPfreeBufferArray(scip, &cutvars); } } } /* free temporary memory */ SCIPfreeBufferArrayNull(scip, &varsolvals); SCIPfreeBufferArray(scip, &inds); SCIPfreeBufferArray(scip, &binvrow); SCIPfreeBufferArray(scip, &basisind); SCIPfreeBufferArray(scip, &cutcoefs); SCIPdebugMessage("end searching strong CG cuts: found %d cuts\n", ncuts); sepadata->lastncutsfound = SCIPgetNCutsFound(scip); return SCIP_OKAY; }
/** propagate the given none binary variable/column using the reduced cost */ static SCIP_RETCODE propagateRedcostVar( SCIP* scip, /**< SCIP data structure */ SCIP_VAR* var, /**< variable to use for propagation */ SCIP_COL* col, /**< LP column of the variable */ SCIP_Real lpobjval, /**< objective value of the current LP */ SCIP_Real cutoffbound, /**< the current cutoff bound */ int* nchgbds /**< pointer to count the number of bound changes */ ) { SCIP_Real redcost; switch( SCIPcolGetBasisStatus(col) ) { case SCIP_BASESTAT_LOWER: redcost = SCIPgetColRedcost(scip, col); assert(!SCIPisFeasNegative(scip, redcost) || SCIPisFeasEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) ); if( SCIPisFeasPositive(scip, redcost) ) { SCIP_Real oldlb; SCIP_Real oldub; oldlb = SCIPvarGetLbLocal(var); oldub = SCIPvarGetUbLocal(var); assert(SCIPisEQ(scip, oldlb, SCIPcolGetLb(col))); assert(SCIPisEQ(scip, oldub, SCIPcolGetUb(col))); if( SCIPisFeasLT(scip, oldlb, oldub) ) { SCIP_Real newub; SCIP_Bool strengthen; /* calculate reduced cost based bound */ newub = (cutoffbound - lpobjval) / redcost + oldlb; /* check, if new bound is good enough: * - integer variables: take all possible strengthenings * - continuous variables: strengthening must cut part of the variable's dynamic range, and * at least 20% of the current domain */ if( SCIPvarIsIntegral(var) ) { newub = SCIPadjustedVarUb(scip, var, newub); strengthen = (newub < oldub - 0.5); } else strengthen = (newub < SCIPcolGetMaxPrimsol(col) && newub <= 0.2 * oldlb + 0.8 * oldub); if( strengthen ) { /* strengthen upper bound */ SCIPdebugMessage("redcost strengthening upper bound: <%s> [%g,%g] -> [%g,%g] (ub=%g, lb=%g, redcost=%g)\n", SCIPvarGetName(var), oldlb, oldub, oldlb, newub, cutoffbound, lpobjval, redcost); SCIP_CALL( SCIPchgVarUb(scip, var, newub) ); (*nchgbds)++; } } } break; case SCIP_BASESTAT_BASIC: break; case SCIP_BASESTAT_UPPER: redcost = SCIPgetColRedcost(scip, col); assert(!SCIPisFeasPositive(scip, redcost) || SCIPisFeasEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) ); if( SCIPisFeasNegative(scip, redcost) ) { SCIP_Real oldlb; SCIP_Real oldub; oldlb = SCIPvarGetLbLocal(var); oldub = SCIPvarGetUbLocal(var); assert(SCIPisEQ(scip, oldlb, SCIPcolGetLb(col))); assert(SCIPisEQ(scip, oldub, SCIPcolGetUb(col))); if( SCIPisFeasLT(scip, oldlb, oldub) ) { SCIP_Real newlb; SCIP_Bool strengthen; /* calculate reduced cost based bound */ newlb = (cutoffbound - lpobjval) / redcost + oldub; /* check, if new bound is good enough: * - integer variables: take all possible strengthenings * - continuous variables: strengthening must cut part of the variable's dynamic range, and * at least 20% of the current domain */ if( SCIPvarIsIntegral(var) ) { newlb = SCIPadjustedVarLb(scip, var, newlb); strengthen = (newlb > oldlb + 0.5); } else strengthen = (newlb > SCIPcolGetMinPrimsol(col) && newlb >= 0.8 * oldlb + 0.2 * oldub); /* check, if new bound is good enough: at least 20% strengthening for continuous variables */ if( strengthen ) { /* strengthen lower bound */ SCIPdebugMessage("redcost strengthening lower bound: <%s> [%g,%g] -> [%g,%g] (ub=%g, lb=%g, redcost=%g)\n", SCIPvarGetName(var), oldlb, oldub, newlb, oldub, cutoffbound, lpobjval, redcost); SCIP_CALL( SCIPchgVarLb(scip, var, newlb) ); (*nchgbds)++; } } } break; case SCIP_BASESTAT_ZERO: assert(SCIPisFeasZero(scip, SCIPgetColRedcost(scip, col))); break; default: SCIPerrorMessage("invalid basis state\n"); return SCIP_INVALIDDATA; } return SCIP_OKAY; }
/** returns a variable, that pushes activity of the row in the given direction with minimal negative impact on other rows; * if variables have equal impact, chooses the one with best objective value improvement in corresponding direction; * prefer fractional integers over other variables in order to become integral during the process; * shifting in a direction is forbidden, if this forces the objective value over the upper bound, or if the variable * was already shifted in the opposite direction */ static SCIP_RETCODE selectShifting( SCIP* scip, /**< SCIP data structure */ SCIP_SOL* sol, /**< primal solution */ SCIP_ROW* row, /**< LP row */ SCIP_Real rowactivity, /**< activity of LP row */ int direction, /**< should the activity be increased (+1) or decreased (-1)? */ SCIP_Real* nincreases, /**< array with weighted number of increasings per variables */ SCIP_Real* ndecreases, /**< array with weighted number of decreasings per variables */ SCIP_Real increaseweight, /**< current weight of increase/decrease updates */ SCIP_VAR** shiftvar, /**< pointer to store the shifting variable, returns NULL if impossible */ SCIP_Real* oldsolval, /**< pointer to store old solution value of shifting variable */ SCIP_Real* newsolval /**< pointer to store new (shifted) solution value of shifting variable */ ) { SCIP_COL** rowcols; SCIP_Real* rowvals; int nrowcols; SCIP_Real activitydelta; SCIP_Real bestshiftscore; SCIP_Real bestdeltaobj; int c; assert(direction == +1 || direction == -1); assert(nincreases != NULL); assert(ndecreases != NULL); assert(shiftvar != NULL); assert(oldsolval != NULL); assert(newsolval != NULL); /* get row entries */ rowcols = SCIProwGetCols(row); rowvals = SCIProwGetVals(row); nrowcols = SCIProwGetNLPNonz(row); /* calculate how much the activity must be shifted in order to become feasible */ activitydelta = (direction == +1 ? SCIProwGetLhs(row) - rowactivity : SCIProwGetRhs(row) - rowactivity); assert((direction == +1 && SCIPisPositive(scip, activitydelta)) || (direction == -1 && SCIPisNegative(scip, activitydelta))); /* select shifting variable */ bestshiftscore = SCIP_REAL_MAX; bestdeltaobj = SCIPinfinity(scip); *shiftvar = NULL; *newsolval = 0.0; *oldsolval = 0.0; for( c = 0; c < nrowcols; ++c ) { SCIP_COL* col; SCIP_VAR* var; SCIP_Real val; SCIP_Real solval; SCIP_Real shiftval; SCIP_Real shiftscore; SCIP_Bool isinteger; SCIP_Bool isfrac; SCIP_Bool increase; col = rowcols[c]; var = SCIPcolGetVar(col); val = rowvals[c]; assert(!SCIPisZero(scip, val)); solval = SCIPgetSolVal(scip, sol, var); isinteger = (SCIPvarGetType(var) == SCIP_VARTYPE_BINARY || SCIPvarGetType(var) == SCIP_VARTYPE_INTEGER); isfrac = isinteger && !SCIPisFeasIntegral(scip, solval); increase = (direction * val > 0.0); /* calculate the score of the shifting (prefer smaller values) */ if( isfrac ) shiftscore = increase ? -1.0 / (SCIPvarGetNLocksUp(var) + 1.0) : -1.0 / (SCIPvarGetNLocksDown(var) + 1.0); else { int probindex; probindex = SCIPvarGetProbindex(var); if( increase ) shiftscore = ndecreases[probindex]/increaseweight; else shiftscore = nincreases[probindex]/increaseweight; if( isinteger ) shiftscore += 1.0; } if( shiftscore <= bestshiftscore ) { SCIP_Real deltaobj; if( !increase ) { /* shifting down */ assert(direction * val < 0.0); if( isfrac ) shiftval = SCIPfeasFloor(scip, solval); else { SCIP_Real lb; assert(activitydelta/val < 0.0); shiftval = solval + activitydelta/val; assert(shiftval <= solval); /* may be equal due to numerical digit erasement in the subtraction */ if( SCIPvarIsIntegral(var) ) shiftval = SCIPfeasFloor(scip, shiftval); lb = SCIPvarGetLbGlobal(var); shiftval = MAX(shiftval, lb); } } else { /* shifting up */ assert(direction * val > 0.0); if( isfrac ) shiftval = SCIPfeasCeil(scip, solval); else { SCIP_Real ub; assert(activitydelta/val > 0.0); shiftval = solval + activitydelta/val; assert(shiftval >= solval); /* may be equal due to numerical digit erasement in the subtraction */ if( SCIPvarIsIntegral(var) ) shiftval = SCIPfeasCeil(scip, shiftval); ub = SCIPvarGetUbGlobal(var); shiftval = MIN(shiftval, ub); } } if( SCIPisEQ(scip, shiftval, solval) ) continue; deltaobj = SCIPvarGetObj(var) * (shiftval - solval); if( shiftscore < bestshiftscore || deltaobj < bestdeltaobj ) { bestshiftscore = shiftscore; bestdeltaobj = deltaobj; *shiftvar = var; *oldsolval = solval; *newsolval = shiftval; } } } return SCIP_OKAY; }