/** find variable aggregations for uplock case */ static SCIP_RETCODE findUplockAggregations( SCIP* scip, /**< SCIP main data structure */ SCIPMILPMATRIX* matrix, /**< constraint matrix */ int* nvaragg, /**< number of redundant variables */ AGGRTYPE* aggtypes, /**< type of aggregations (in same order as variables in matrix) */ SCIP_VAR** binvars /**< pointers to the binary variables (in same order as variables in matrix) */ ) { int nvars; int i; assert(scip != NULL); assert(matrix != NULL); assert(nvaragg != NULL); assert(aggtypes != NULL); assert(binvars != NULL); nvars = SCIPmatrixGetNColumns(matrix); for( i = 0; i < nvars; i++ ) { /* column has only one uplock which keeps it from being fixed by duality fixing */ if( SCIPmatrixGetColNUplocks(matrix, i) == 1 && SCIPisLE(scip, SCIPvarGetObj(SCIPmatrixGetVar(matrix, i)), 0.0) ) { SCIP_Real lb; SCIP_Real ub; lb = SCIPmatrixGetColLb(matrix, i); ub = SCIPmatrixGetColUb(matrix, i); assert(lb == SCIPvarGetLbGlobal(SCIPmatrixGetVar(matrix, i))); /*lint !e777*/ assert(ub == SCIPvarGetUbGlobal(SCIPmatrixGetVar(matrix, i))); /*lint !e777*/ /* the variable needs to have finite bounds to allow an agregation */ if( !SCIPisInfinity(scip, -lb) && !SCIPisInfinity(scip, ub) ) { int binvaridx; AGGRTYPE aggtype; getBinVarIdxInUplockRow(scip, matrix, i, &binvaridx, &aggtype); if( binvaridx >= 0 ) { aggtypes[i] = aggtype; binvars[i] = SCIPmatrixGetVar(matrix, binvaridx); (*nvaragg)++; } } } } return SCIP_OKAY; }
/** perform dual presolving */ static SCIP_RETCODE performDualfix( SCIP* scip, /**< SCIP data structure */ int* nfixedvars, /**< pointer to store number of fixed variables */ SCIP_Bool* unbounded, /**< pointer to store if an unboundness was detected */ SCIP_Bool* cutoff /**< pointer to store if a cutoff was detected */ ) { SCIP_VAR** vars; int nvars; int v; /* get active problem variables */ vars = SCIPgetVars(scip); nvars = SCIPgetNVars(scip); /* look for fixable variables * loop backwards, since a variable fixing can change the current and the subsequent slots in the vars array */ for( v = nvars - 1; v >= 0; --v ) { SCIP_VAR* var; SCIP_Real bound; SCIP_Real obj; SCIP_Bool infeasible; SCIP_Bool fixed; var = vars[v]; assert(var != NULL); /* don't perform dual presolving operations on deleted variables */ if( SCIPvarIsDeleted(var) ) continue; /* ignore already fixed variables (use feasibility tolerance since this is used in SCIPfixVar() */ if( SCIPisFeasEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) ) continue; obj = SCIPvarGetObj(var); /* if the objective coefficient of the variable is 0 and it may be rounded both * up and down, then fix it to the closest feasible value to 0 */ if( SCIPisZero(scip, obj) && SCIPvarMayRoundDown(var) && SCIPvarMayRoundUp(var) ) { SCIP_Real roundbound; bound = SCIPvarGetLbGlobal(var); if( SCIPisLT(scip, bound, 0.0) ) { if( SCIPisLE(scip, 0.0, SCIPvarGetUbGlobal(var)) ) bound = 0.0; else { /* try to take an integer value, only for polishing */ roundbound = SCIPfloor(scip, SCIPvarGetUbGlobal(var)); if( roundbound < bound ) bound = SCIPvarGetUbGlobal(var); else bound = roundbound; } } else { /* try to take an integer value, only for polishing */ roundbound = SCIPceil(scip, bound); if( roundbound < SCIPvarGetUbGlobal(var) ) bound = roundbound; } SCIPdebugMessage("fixing variable <%s> with objective 0 to %g\n", SCIPvarGetName(var), bound); } else { /* if it is always possible to round variable in direction of objective value, fix it to its proper bound */ if( SCIPvarMayRoundDown(var) && !SCIPisNegative(scip, obj) ) { bound = SCIPvarGetLbGlobal(var); if ( SCIPisInfinity(scip, -bound) ) { /* variable can be fixed to -infinity */ if ( SCIPgetStage(scip) > SCIP_STAGE_PRESOLVING ) { /* Fixing variables to infinity is not allowed after presolving, since LP-solvers cannot handle this * consistently. We thus have to ignore this (should better be handled in presolving). */ continue; } if ( SCIPisZero(scip, obj) && SCIPvarGetNLocksUp(var) == 1 ) { /* Variable is only contained in one constraint: we hope that the corresponding constraint handler is * clever enough to set/aggregate the variable to something more useful than -infinity and do nothing * here. */ continue; } } SCIPdebugMessage("fixing variable <%s> with objective %g and %d uplocks to lower bound %g\n", SCIPvarGetName(var), SCIPvarGetObj(var), SCIPvarGetNLocksUp(var), bound); } else if( SCIPvarMayRoundUp(var) && !SCIPisPositive(scip, obj) ) { bound = SCIPvarGetUbGlobal(var); if ( SCIPisInfinity(scip, bound) ) { /* variable can be fixed to infinity */ if ( SCIPgetStage(scip) > SCIP_STAGE_PRESOLVING ) { /* Fixing variables to infinity is not allowed after presolving, since LP-solvers cannot handle this * consistently. We thus have to ignore this (should better be handled in presolving). */ continue; } if ( SCIPisZero(scip, obj) && SCIPvarGetNLocksDown(var) == 1 ) { /* Variable is only contained in one constraint: we hope that the corresponding constraint handler is * clever enough to set/aggregate the variable to something more useful than +infinity and do nothing * here */ continue; } } SCIPdebugMessage("fixing variable <%s> with objective %g and %d downlocks to upper bound %g\n", SCIPvarGetName(var), SCIPvarGetObj(var), SCIPvarGetNLocksDown(var), bound); } else continue; } if( SCIPisInfinity(scip, REALABS(bound)) && !SCIPisZero(scip, obj) ) { SCIPdebugMessage(" -> unbounded fixing\n"); SCIPverbMessage(scip, SCIP_VERBLEVEL_NORMAL, NULL, "problem infeasible or unbounded: variable <%s> with objective %.15g can be made infinitely %s\n", SCIPvarGetName(var), SCIPvarGetObj(var), bound < 0.0 ? "small" : "large"); *unbounded = TRUE; return SCIP_OKAY; } /* apply the fixing */ SCIPdebugMessage("apply fixing of variable %s to %g\n", SCIPvarGetName(var), bound); SCIP_CALL( SCIPfixVar(scip, var, bound, &infeasible, &fixed) ); if( infeasible ) { SCIPdebugMessage(" -> infeasible fixing\n"); *cutoff = TRUE; return SCIP_OKAY; } assert(fixed || (SCIPgetStage(scip) == SCIP_STAGE_SOLVING && SCIPisFeasEQ(scip, bound, SCIPvarGetLbLocal(var)) && SCIPisFeasEQ(scip, bound, SCIPvarGetUbLocal(var)))); (*nfixedvars)++; } return SCIP_OKAY; }
/** creates a subproblem for subscip by fixing a number of variables */ static SCIP_RETCODE createSubproblem( SCIP* scip, /**< original SCIP data structure */ SCIP* subscip, /**< SCIP data structure for the subproblem */ SCIP_VAR** subvars, /**< the variables of the subproblem */ SCIP_Real minfixingrate, /**< percentage of integer variables that have to be fixed */ unsigned int* randseed, /**< a seed value for the random number generator */ SCIP_Bool uselprows /**< should subproblem be created out of the rows in the LP rows? */ ) { SCIP_VAR** vars; /* original scip variables */ SCIP_SOL* sol; /* pool of solutions */ SCIP_Bool* marked; /* array of markers, which variables to fixed */ SCIP_Bool fixingmarker; /* which flag should label a fixed variable? */ int nvars; int nbinvars; int nintvars; int i; int j; int nmarkers; /* get required data of the original problem */ SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, &nbinvars, &nintvars, NULL, NULL) ); sol = SCIPgetBestSol(scip); assert(sol != NULL); SCIP_CALL( SCIPallocBufferArray(scip, &marked, nbinvars+nintvars) ); if( minfixingrate > 0.5 ) { nmarkers = nbinvars + nintvars - (int) SCIPfloor(scip, minfixingrate*(nbinvars+nintvars)); fixingmarker = FALSE; } else { nmarkers = (int) SCIPceil(scip, minfixingrate*(nbinvars+nintvars)); fixingmarker = TRUE; } assert( 0 <= nmarkers && nmarkers <= SCIPceil(scip,(nbinvars+nintvars)/2.0 ) ); j = 0; BMSclearMemoryArray(marked, nbinvars+nintvars); while( j < nmarkers ) { do { i = SCIPgetRandomInt(0, nbinvars+nintvars-1, randseed); } while( marked[i] ); marked[i] = TRUE; j++; } assert( j == nmarkers ); /* change bounds of variables of the subproblem */ for( i = 0; i < nbinvars + nintvars; i++ ) { /* fix all randomly marked variables */ if( marked[i] == fixingmarker ) { SCIP_Real solval; SCIP_Real lb; SCIP_Real ub; solval = SCIPgetSolVal(scip, sol, vars[i]); lb = SCIPvarGetLbGlobal(subvars[i]); ub = SCIPvarGetUbGlobal(subvars[i]); assert(SCIPisLE(scip, lb, ub)); /* due to dual reductions, it may happen that the solution value is not in the variable's domain anymore */ if( SCIPisLT(scip, solval, lb) ) solval = lb; else if( SCIPisGT(scip, solval, ub) ) solval = ub; /* perform the bound change */ if( !SCIPisInfinity(scip, solval) && !SCIPisInfinity(scip, -solval) ) { SCIP_CALL( SCIPchgVarLbGlobal(subscip, subvars[i], solval) ); SCIP_CALL( SCIPchgVarUbGlobal(subscip, subvars[i], solval) ); } } } if( uselprows ) { SCIP_ROW** rows; /* original scip rows */ int nrows; /* get the rows and their number */ SCIP_CALL( SCIPgetLPRowsData(scip, &rows, &nrows) ); /* copy all rows to linear constraints */ for( i = 0; i < nrows; i++ ) { SCIP_CONS* cons; SCIP_VAR** consvars; SCIP_COL** cols; SCIP_Real constant; SCIP_Real lhs; SCIP_Real rhs; SCIP_Real* vals; int nnonz; /* ignore rows that are only locally valid */ if( SCIProwIsLocal(rows[i]) ) continue; /* get the row's data */ constant = SCIProwGetConstant(rows[i]); lhs = SCIProwGetLhs(rows[i]) - constant; rhs = SCIProwGetRhs(rows[i]) - constant; vals = SCIProwGetVals(rows[i]); nnonz = SCIProwGetNNonz(rows[i]); cols = SCIProwGetCols(rows[i]); assert( lhs <= rhs ); /* allocate memory array to be filled with the corresponding subproblem variables */ SCIP_CALL( SCIPallocBufferArray(scip, &consvars, nnonz) ); for( j = 0; j < nnonz; j++ ) consvars[j] = subvars[SCIPvarGetProbindex(SCIPcolGetVar(cols[j]))]; /* create a new linear constraint and add it to the subproblem */ SCIP_CALL( SCIPcreateConsLinear(subscip, &cons, SCIProwGetName(rows[i]), nnonz, consvars, vals, lhs, rhs, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE) ); SCIP_CALL( SCIPaddCons(subscip, cons) ); SCIP_CALL( SCIPreleaseCons(subscip, &cons) ); /* free temporary memory */ SCIPfreeBufferArray(scip, &consvars); } } SCIPfreeBufferArray(scip, &marked); return SCIP_OKAY; }
/** 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; }
/** execution method of presolver */ static SCIP_DECL_PRESOLEXEC(presolExecDualfix) { /*lint --e{715}*/ SCIP_VAR** vars; SCIP_Real bound; SCIP_Real roundbound; SCIP_Real obj; SCIP_Bool infeasible; SCIP_Bool fixed; int nvars; int v; assert(presol != NULL); assert(strcmp(SCIPpresolGetName(presol), PRESOL_NAME) == 0); assert(result != NULL); *result = SCIP_DIDNOTFIND; /* get active problem variables */ vars = SCIPgetVars(scip); nvars = SCIPgetNVars(scip); /* look for fixable variables * loop backwards, since a variable fixing can change the current and the subsequent slots in the vars array */ for( v = nvars - 1; v >= 0; --v ) { /* don't perform dual presolving operations on deleted variables */ if( SCIPvarIsDeleted(vars[v]) ) continue; obj = SCIPvarGetObj(vars[v]); /* if the objective coefficient of the variable is 0 and it may be rounded both * up and down, then fix it to the closest feasible value to 0 */ if( SCIPisZero(scip, obj) && SCIPvarMayRoundDown(vars[v]) && SCIPvarMayRoundUp(vars[v]) ) { bound = SCIPvarGetLbGlobal(vars[v]); if( SCIPisLT(scip, bound, 0.0) ) { if( SCIPisLE(scip, 0.0, SCIPvarGetUbGlobal(vars[v])) ) bound = 0.0; else { /* try to take an integer value, only for polishing */ roundbound = SCIPfloor(scip, SCIPvarGetUbGlobal(vars[v])); if( roundbound < bound ) bound = SCIPvarGetUbGlobal(vars[v]); else bound = roundbound; } } else { /* try to take an integer value, only for polishing */ roundbound = SCIPceil(scip, bound); if( roundbound < SCIPvarGetUbGlobal(vars[v]) ) bound = roundbound; } SCIPdebugMessage("variable <%s> with objective 0 fixed to %g\n", SCIPvarGetName(vars[v]), bound); } else { /* if it is always possible to round variable in direction of objective value, * fix it to its proper bound */ if( SCIPvarMayRoundDown(vars[v]) && !SCIPisNegative(scip, obj) ) { bound = SCIPvarGetLbGlobal(vars[v]); if( SCIPisZero(scip, obj) && SCIPvarGetNLocksUp(vars[v]) == 1 && SCIPisInfinity(scip, -bound) ) { /* variable can be set to -infinity, and it is only contained in one constraint: * we hope that the corresponding constraint handler is clever enough to set/aggregate the variable * to something more useful than -infinity and do nothing here */ continue; } SCIPdebugMessage("variable <%s> with objective %g and %d uplocks fixed to lower bound %g\n", SCIPvarGetName(vars[v]), SCIPvarGetObj(vars[v]), SCIPvarGetNLocksUp(vars[v]), bound); } else if( SCIPvarMayRoundUp(vars[v]) && !SCIPisPositive(scip, obj) ) { bound = SCIPvarGetUbGlobal(vars[v]); if( SCIPisZero(scip, obj) && SCIPvarGetNLocksDown(vars[v]) == 1 && SCIPisInfinity(scip, bound) ) { /* variable can be set to +infinity, and it is only contained in one constraint: * we hope that the corresponding constraint handler is clever enough to set/aggregate the variable * to something more useful than +infinity and do nothing here */ continue; } SCIPdebugMessage("variable <%s> with objective %g and %d downlocks fixed to upper bound %g\n", SCIPvarGetName(vars[v]), SCIPvarGetObj(vars[v]), SCIPvarGetNLocksDown(vars[v]), bound); } else continue; } /* apply the fixing */ if( SCIPisInfinity(scip, REALABS(bound)) && !SCIPisZero(scip, obj) ) { SCIPdebugMessage(" -> unbounded fixing\n"); SCIPverbMessage(scip, SCIP_VERBLEVEL_NORMAL, NULL, "problem infeasible or unbounded: variable <%s> with objective %.15g can be made infinitely %s\n", SCIPvarGetName(vars[v]), SCIPvarGetObj(vars[v]), bound < 0.0 ? "small" : "large"); *result = SCIP_UNBOUNDED; return SCIP_OKAY; } SCIP_CALL( SCIPfixVar(scip, vars[v], bound, &infeasible, &fixed) ); if( infeasible ) { SCIPdebugMessage(" -> infeasible fixing\n"); *result = SCIP_CUTOFF; return SCIP_OKAY; } assert(fixed); (*nfixedvars)++; *result = SCIP_SUCCESS; } return SCIP_OKAY; }
/** presolving execution method */ static SCIP_DECL_PRESOLEXEC(presolExecBoundshift) { /*lint --e{715}*/ SCIP_PRESOLDATA* presoldata; SCIP_VAR** scipvars; SCIP_VAR** vars; int nbinvars; int nvars; int v; assert(scip != NULL); assert(presol != NULL); assert(strcmp(SCIPpresolGetName(presol), PRESOL_NAME) == 0); assert(result != NULL); *result = SCIP_DIDNOTRUN; /* get presolver data */ presoldata = SCIPpresolGetData(presol); assert(presoldata != NULL); /* get the problem variables */ scipvars = SCIPgetVars(scip); nbinvars = SCIPgetNBinVars(scip); nvars = SCIPgetNVars(scip) - nbinvars; if( nvars == 0 ) return SCIP_OKAY; if( SCIPdoNotAggr(scip) ) return SCIP_OKAY; *result = SCIP_DIDNOTFIND; /* copy the integer variables into an own array, since adding new integer variables affects the left-most slots in * the array and thereby interferes with our search loop */ SCIP_CALL( SCIPduplicateBufferArray(scip, &vars, &scipvars[nbinvars], nvars) ); /* scan the integer, implicit, and continuous variables for possible conversion */ for( v = nvars - 1; v >= 0; --v ) { SCIP_VAR* var = vars[v]; SCIP_Real lb; SCIP_Real ub; assert(SCIPvarGetType(var) != SCIP_VARTYPE_BINARY); /* get current variable's bounds */ lb = SCIPvarGetLbGlobal(var); ub = SCIPvarGetUbGlobal(var); assert( SCIPisLE(scip, lb, ub) ); if( SCIPisEQ(scip, lb, ub) ) continue; if( presoldata->integer && !SCIPisIntegral(scip, ub - lb) ) continue; /* check if bounds are shiftable */ if( !SCIPisEQ(scip, lb, 0.0) && /* lower bound != 0.0 */ SCIPisLT(scip, ub, SCIPinfinity(scip)) && /* upper bound != infinity */ SCIPisGT(scip, lb, -SCIPinfinity(scip)) && /* lower bound != -infinity */ #if 0 SCIPisLT(scip, ub - lb, SCIPinfinity(scip)) && /* interval length less than SCIPinfinity(scip) */ #endif SCIPisLT(scip, ub - lb, (SCIP_Real) presoldata->maxshift) ) /* less than max shifting */ { SCIP_VAR* newvar; char newvarname[SCIP_MAXSTRLEN]; SCIP_Bool infeasible; SCIP_Bool redundant; SCIP_Bool aggregated; SCIPdebugMessage("convert range <%s>[%g,%g] to [%g,%g]\n", SCIPvarGetName(var), lb, ub, 0.0, (ub - lb) ); /* create new variable */ (void) SCIPsnprintf(newvarname, SCIP_MAXSTRLEN, "%s_shift", SCIPvarGetName(var)); SCIP_CALL( SCIPcreateVar(scip, &newvar, newvarname, 0.0, (ub - lb), 0.0, SCIPvarGetType(var), SCIPvarIsInitial(var), SCIPvarIsRemovable(var), NULL, NULL, NULL, NULL, NULL) ); SCIP_CALL( SCIPaddVar(scip, newvar) ); /* aggregate old variable with new variable */ if( presoldata->flipping ) { if( REALABS(ub) < REALABS(lb) ) { SCIP_CALL( SCIPaggregateVars(scip, var, newvar, 1.0, 1.0, ub, &infeasible, &redundant, &aggregated) ); } else { SCIP_CALL( SCIPaggregateVars(scip, var, newvar, 1.0, -1.0, lb, &infeasible, &redundant, &aggregated) ); } } else { SCIP_CALL( SCIPaggregateVars(scip, var, newvar, 1.0, -1.0, lb, &infeasible, &redundant, &aggregated) ); } assert(!infeasible); assert(redundant); assert(aggregated); SCIPdebugMessage("var <%s> with bounds [%f,%f] has obj %f\n", SCIPvarGetName(newvar),SCIPvarGetLbGlobal(newvar),SCIPvarGetUbGlobal(newvar),SCIPvarGetObj(newvar)); /* release variable */ SCIP_CALL( SCIPreleaseVar(scip, &newvar) ); /* take care of statistic */ (*naggrvars)++; *result = SCIP_SUCCESS; } } /* free temporary memory */ SCIPfreeBufferArray(scip, &vars); return SCIP_OKAY; }
/** compares the so far best branching candidate with a new candidate and updates best candidate, if new candidate is better */ static void updateBestCandidate( SCIP* scip, /**< SCIP data structure */ SCIP_VAR** bestvar, /**< best branching candidate */ SCIP_Real* bestscore, /**< score of best branching candidate */ SCIP_Real* bestobj, /**< absolute objective value of best branching candidate */ SCIP_Real* bestsol, /**< proposed branching point of best branching candidate */ SCIP_VAR* cand, /**< branching candidate to consider */ SCIP_Real candscore, /**< scoring of branching candidate */ SCIP_Real candsol /**< proposed branching point of branching candidate */ ) { SCIP_Real obj; assert(scip != NULL); assert(bestvar != NULL); assert(bestscore != NULL); assert(bestobj != NULL); assert(*bestobj >= 0.0); assert(cand != NULL); /* a branching variable candidate should either be an active problem variable or a multi-aggregated variable */ assert(SCIPvarIsActive(SCIPvarGetProbvar(cand)) || SCIPvarGetStatus(SCIPvarGetProbvar(cand)) == SCIP_VARSTATUS_MULTAGGR); if( SCIPvarGetStatus(SCIPvarGetProbvar(cand)) == SCIP_VARSTATUS_MULTAGGR ) { /* for a multi-aggregated variable, we call updateBestCandidate function recursively with all variables in the multi-aggregation */ SCIP_VAR** multvars; int nmultvars; int i; SCIP_Bool success; SCIP_Real multvarlb; SCIP_Real multvarub; cand = SCIPvarGetProbvar(cand); multvars = SCIPvarGetMultaggrVars(cand); nmultvars = SCIPvarGetMultaggrNVars(cand); /* if we have a candidate branching point, then first register only aggregation variables * for which we can compute a corresponding branching point too (see also comments below) * if this fails, then register all (unfixed) aggregation variables, thereby forgetting about candsol */ success = FALSE; if( candsol != SCIP_INVALID ) /*lint !e777*/ { SCIP_Real* multscalars; SCIP_Real minact; SCIP_Real maxact; SCIP_Real aggrvarsol; SCIP_Real aggrvarsol1; SCIP_Real aggrvarsol2; multscalars = SCIPvarGetMultaggrScalars(cand); /* for computing the branching point, we need the current bounds of the multi-aggregated variable */ minact = SCIPcomputeVarLbLocal(scip, cand); maxact = SCIPcomputeVarUbLocal(scip, cand); for( i = 0; i < nmultvars; ++i ) { /* skip fixed variables */ multvarlb = SCIPcomputeVarLbLocal(scip, multvars[i]); multvarub = SCIPcomputeVarUbLocal(scip, multvars[i]); if( SCIPisEQ(scip, multvarlb, multvarub) ) continue; assert(multscalars != NULL); assert(multscalars[i] != 0.0); /* we cannot ensure that both the upper bound in the left node and the lower bound in the right node * will be candsol by a clever choice for the branching point of multvars[i], * but we can try to ensure that at least one of them will be at candsol */ if( multscalars[i] > 0.0 ) { /* cand >= candsol * if multvars[i] >= (candsol - (maxact - multscalars[i] * ub(multvars[i]))) / multscalars[i] * = (candsol - maxact) / multscalars[i] + ub(multvars[i]) */ aggrvarsol1 = (candsol - maxact) / multscalars[i] + multvarub; /* cand <= candsol * if multvars[i] <= (candsol - (minact - multscalar[i] * lb(multvars[i]))) / multscalars[i] * = (candsol - minact) / multscalars[i] + lb(multvars[i]) */ aggrvarsol2 = (candsol - minact) / multscalars[i] + multvarlb; } else { /* cand >= candsol * if multvars[i] <= (candsol - (maxact - multscalars[i] * lb(multvars[i]))) / multscalars[i] * = (candsol - maxact) / multscalars[i] + lb(multvars[i]) */ aggrvarsol2 = (candsol - maxact) / multscalars[i] + multvarlb; /* cand <= candsol * if multvars[i] >= (candsol - (minact - multscalar[i] * ub(multvars[i]))) / multscalars[i] * = (candsol - minact) / multscalars[i] + ub(multvars[i]) */ aggrvarsol1 = (candsol - minact) / multscalars[i] + multvarub; } /* by the above choice, aggrvarsol1 <= ub(multvars[i]) and aggrvarsol2 >= lb(multvars[i]) * if aggrvarsol1 <= lb(multvars[i]) or aggrvarsol2 >= ub(multvars[i]), then choose the other one * if both are out of bounds, then give up * if both are inside bounds, then choose the one closer to 0.0 (someone has better idea???) */ if( SCIPisFeasLE(scip, aggrvarsol1, multvarlb) ) { if( SCIPisFeasGE(scip, aggrvarsol2, multvarub) ) continue; else aggrvarsol = aggrvarsol2; } else { if( SCIPisFeasGE(scip, aggrvarsol2, multvarub) ) aggrvarsol = aggrvarsol1; else aggrvarsol = REALABS(aggrvarsol1) < REALABS(aggrvarsol2) ? aggrvarsol1 : aggrvarsol2; } success = TRUE; updateBestCandidate(scip, bestvar, bestscore, bestobj, bestsol, multvars[i], candscore, aggrvarsol); } } if( !success ) for( i = 0; i < nmultvars; ++i ) { /* skip fixed variables */ multvarlb = SCIPcomputeVarLbLocal(scip, multvars[i]); multvarub = SCIPcomputeVarUbLocal(scip, multvars[i]); if( SCIPisEQ(scip, multvarlb, multvarub) ) continue; updateBestCandidate(scip, bestvar, bestscore, bestobj, bestsol, multvars[i], candscore, SCIP_INVALID); } assert(*bestvar != NULL); /* if all variables were fixed, something is strange */ return; } candscore *= SCIPvarGetBranchFactor(cand); obj = SCIPvarGetObj(cand); obj = REALABS(obj); if( SCIPisInfinity(scip, *bestscore) || (!SCIPisInfinity(scip, candscore) && (SCIPisLT(scip, candscore, *bestscore) || (SCIPisLE(scip, candscore, *bestscore) && obj > *bestobj))) ) { *bestvar = cand; *bestscore = candscore; *bestobj = obj; *bestsol = candsol; } }
/** execution method of primal heuristic */ static SCIP_DECL_HEUREXEC(heurExecTrivial) { /*lint --e{715}*/ SCIP_VAR** vars; SCIP_SOL* lbsol; /* solution where all variables are set to their lower bounds */ SCIP_SOL* ubsol; /* solution where all variables are set to their upper bounds */ SCIP_SOL* zerosol; /* solution where all variables are set to zero */ SCIP_SOL* locksol; /* solution where all variables are set to the bound with the fewer locks */ SCIP_Real large; int nvars; int nbinvars; int i; SCIP_Bool success; SCIP_Bool zerovalid; *result = SCIP_DIDNOTRUN; if( SCIPgetNRuns(scip) > 1 ) return SCIP_OKAY; *result = SCIP_DIDNOTFIND; success = FALSE; /* initialize data structure */ SCIP_CALL( SCIPcreateSol(scip, &lbsol, heur) ); SCIP_CALL( SCIPcreateSol(scip, &ubsol, heur) ); SCIP_CALL( SCIPcreateSol(scip, &zerosol, heur) ); SCIP_CALL( SCIPcreateSol(scip, &locksol, heur) ); /* determine large value to set variables to */ large = SCIPinfinity(scip); if( !SCIPisInfinity(scip, 0.1 / SCIPfeastol(scip)) ) large = 0.1 / SCIPfeastol(scip); SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, &nbinvars, NULL, NULL, NULL) ); /* if the problem is binary, we do not have to check the zero solution, since it is equal to the lower bound * solution */ zerovalid = (nvars != nbinvars); assert(vars != NULL || nvars == 0); for( i = 0; i < nvars; i++ ) { SCIP_Real lb; SCIP_Real ub; assert(vars != NULL); /* this assert is needed for flexelint */ lb = SCIPvarGetLbLocal(vars[i]); ub = SCIPvarGetUbLocal(vars[i]); /* if problem is obviously infeasible due to empty domain, stop */ if( SCIPisGT(scip, lb, ub) ) goto TERMINATE; /* set bounds to sufficient large value */ if( SCIPisInfinity(scip, -lb) ) lb = MIN(-large, ub); if( SCIPisInfinity(scip, ub) ) { SCIP_Real tmp; tmp = SCIPvarGetLbLocal(vars[i]); ub = MAX(tmp, large); } SCIP_CALL( SCIPsetSolVal(scip, lbsol, vars[i], lb) ); SCIP_CALL( SCIPsetSolVal(scip, ubsol, vars[i], ub) ); /* try the zero vector, if it is in the bounds region */ if( zerovalid ) { if( SCIPisLE(scip, lb, 0.0) && SCIPisLE(scip, 0.0, ub) ) { SCIP_CALL( SCIPsetSolVal(scip, zerosol, vars[i], 0.0) ); } else zerovalid = FALSE; } /* set variables to the bound with fewer locks, if tie choose an average value */ if( SCIPvarGetNLocksDown(vars[i]) > SCIPvarGetNLocksUp(vars[i]) ) { SCIP_CALL( SCIPsetSolVal(scip, locksol, vars[i], ub) ); } else if( SCIPvarGetNLocksDown(vars[i]) < SCIPvarGetNLocksUp(vars[i]) ) { SCIP_CALL( SCIPsetSolVal(scip, locksol, vars[i], lb) ); } else { SCIP_Real solval; solval = (lb+ub)/2.0; /* if a tie occurs, roughly every third integer variable will be rounded up */ if( SCIPvarGetType(vars[i]) != SCIP_VARTYPE_CONTINUOUS ) solval = i % 3 == 0 ? SCIPceil(scip,solval) : SCIPfloor(scip,solval); assert(SCIPisFeasLE(scip,SCIPvarGetLbLocal(vars[i]),solval) && SCIPisFeasLE(scip,solval,SCIPvarGetUbLocal(vars[i]))); SCIP_CALL( SCIPsetSolVal(scip, locksol, vars[i], solval) ); } } /* try lower bound solution */ SCIPdebugMessage("try lower bound solution\n"); SCIP_CALL( SCIPtrySol(scip, lbsol, FALSE, FALSE, TRUE, TRUE, &success) ); if( success ) { SCIPdebugMessage("found feasible lower bound solution:\n"); SCIPdebug( SCIP_CALL( SCIPprintSol(scip, lbsol, NULL, FALSE) ) ); *result = SCIP_FOUNDSOL; } /* try upper bound solution */ SCIPdebugMessage("try upper bound solution\n"); SCIP_CALL( SCIPtrySol(scip, ubsol, FALSE, FALSE, TRUE, TRUE, &success) ); if( success ) { SCIPdebugMessage("found feasible upper bound solution:\n"); SCIPdebug( SCIP_CALL( SCIPprintSol(scip, ubsol, NULL, FALSE) ) ); *result = SCIP_FOUNDSOL; } /* try zero solution */ if( zerovalid ) { SCIPdebugMessage("try zero solution\n"); SCIP_CALL( SCIPtrySol(scip, zerosol, FALSE, FALSE, TRUE, TRUE, &success) ); if( success ) { SCIPdebugMessage("found feasible zero solution:\n"); SCIPdebug( SCIP_CALL( SCIPprintSol(scip, zerosol, NULL, FALSE) ) ); *result = SCIP_FOUNDSOL; } } /* try lock solution */ SCIPdebugMessage("try lock solution\n"); SCIP_CALL( SCIPtrySol(scip, locksol, FALSE, FALSE, TRUE, TRUE, &success) ); if( success ) { SCIPdebugMessage("found feasible lock solution:\n"); SCIPdebug( SCIP_CALL( SCIPprintSol(scip, locksol, NULL, FALSE) ) ); *result = SCIP_FOUNDSOL; } TERMINATE: /* free solutions */ SCIP_CALL( SCIPfreeSol(scip, &lbsol) ); SCIP_CALL( SCIPfreeSol(scip, &ubsol) ); SCIP_CALL( SCIPfreeSol(scip, &zerosol) ); SCIP_CALL( SCIPfreeSol(scip, &locksol) ); return SCIP_OKAY; }
/** LP solution separation method of separator */ static SCIP_DECL_SEPAEXECLP(sepaExeclpRapidlearning) {/*lint --e{715}*/ SCIP* subscip; /* the subproblem created by rapid learning */ SCIP_SEPADATA* sepadata; /* separator's private data */ SCIP_VAR** vars; /* original problem's variables */ SCIP_VAR** subvars; /* subproblem's variables */ SCIP_HASHMAP* varmapfw; /* mapping of SCIP variables to sub-SCIP variables */ SCIP_HASHMAP* varmapbw; /* mapping of sub-SCIP variables to SCIP variables */ SCIP_CONSHDLR** conshdlrs; /* array of constraint handler's that might that might obtain conflicts */ int* oldnconss; /* number of constraints without rapid learning conflicts */ SCIP_Longint nodelimit; /* node limit for the subproblem */ SCIP_Real timelimit; /* time limit for the subproblem */ SCIP_Real memorylimit; /* memory limit for the subproblem */ int nconshdlrs; /* size of conshdlr and oldnconss array */ int nfixedvars; /* number of variables that could be fixed by rapid learning */ int nvars; /* number of variables */ int restartnum; /* maximal number of conflicts that should be created */ int i; /* counter */ SCIP_Bool success; /* was problem creation / copying constraint successful? */ SCIP_RETCODE retcode; /* used for catching sub-SCIP errors in debug mode */ int nconflicts; /* statistic: number of conflicts applied */ int nbdchgs; /* statistic: number of bound changes applied */ int n1startinfers; /* statistic: number of one side infer values */ int n2startinfers; /* statistic: number of both side infer values */ SCIP_Bool soladded; /* statistic: was a new incumbent found? */ SCIP_Bool dualboundchg; /* statistic: was a new dual bound found? */ SCIP_Bool disabledualreductions; /* TRUE, if dual reductions in sub-SCIP are not valid for original SCIP, * e.g., because a constraint could not be copied or a primal solution * could not be copied back */ int ndiscvars; soladded = FALSE; assert(sepa != NULL); assert(scip != NULL); assert(result != NULL); *result = SCIP_DIDNOTRUN; ndiscvars = SCIPgetNBinVars(scip) + SCIPgetNIntVars(scip)+SCIPgetNImplVars(scip); /* only run when still not fixed binary variables exists */ if( ndiscvars == 0 ) return SCIP_OKAY; /* get separator's data */ sepadata = SCIPsepaGetData(sepa); assert(sepadata != NULL); /* only run for integer programs */ if( !sepadata->contvars && ndiscvars != SCIPgetNVars(scip) ) return SCIP_OKAY; /* only run if there are few enough continuous variables */ if( sepadata->contvars && SCIPgetNContVars(scip) > sepadata->contvarsquot * SCIPgetNVars(scip) ) return SCIP_OKAY; /* do not run if pricers are present */ if( SCIPgetNActivePricers(scip) > 0 ) return SCIP_OKAY; /* if the separator should be exclusive to the root node, this prevents multiple calls due to restarts */ if( SCIPsepaGetFreq(sepa) == 0 && SCIPsepaGetNCalls(sepa) > 0) return SCIP_OKAY; /* call separator at most once per node */ if( SCIPsepaGetNCallsAtNode(sepa) > 0 ) return SCIP_OKAY; /* do not call rapid learning, if the problem is too big */ if( SCIPgetNVars(scip) > sepadata->maxnvars || SCIPgetNConss(scip) > sepadata->maxnconss ) return SCIP_OKAY; if( SCIPisStopped(scip) ) return SCIP_OKAY; *result = SCIP_DIDNOTFIND; SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL) ); /* initializing the subproblem */ SCIP_CALL( SCIPallocBufferArray(scip, &subvars, nvars) ); SCIP_CALL( SCIPcreate(&subscip) ); SCIP_CALL( SCIPhashmapCreate(&varmapfw, SCIPblkmem(subscip), SCIPcalcHashtableSize(5 * nvars)) ); success = FALSE; /* copy the subproblem */ SCIP_CALL( SCIPcopy(scip, subscip, varmapfw, NULL, "rapid", FALSE, FALSE, &success) ); if( sepadata->copycuts ) { /** copies all active cuts from cutpool of sourcescip to linear constraints in targetscip */ SCIP_CALL( SCIPcopyCuts(scip, subscip, varmapfw, NULL, FALSE) ); } for( i = 0; i < nvars; i++ ) subvars[i] = (SCIP_VAR*) (size_t) SCIPhashmapGetImage(varmapfw, vars[i]); SCIPhashmapFree(&varmapfw); /* this avoids dual presolving */ if( !success ) { for( i = 0; i < nvars; i++ ) { SCIP_CALL( SCIPaddVarLocks(subscip, subvars[i], 1, 1 ) ); } } SCIPdebugMessage("Copying SCIP was%s successful.\n", success ? "" : " not"); /* mimic an FD solver: DFS, no LP solving, 1-FUIP instead of all-FUIP */ SCIP_CALL( SCIPsetIntParam(subscip, "lp/solvefreq", -1) ); SCIP_CALL( SCIPsetIntParam(subscip, "conflict/fuiplevels", 1) ); SCIP_CALL( SCIPsetIntParam(subscip, "nodeselection/dfs/stdpriority", INT_MAX/4) ); SCIP_CALL( SCIPsetBoolParam(subscip, "constraints/disableenfops", TRUE) ); SCIP_CALL( SCIPsetIntParam(subscip, "propagating/pseudoobj/freq", -1) ); /* use inference branching */ SCIP_CALL( SCIPsetBoolParam(subscip, "branching/inference/useweightedsum", FALSE) ); /* only create short conflicts */ SCIP_CALL( SCIPsetRealParam(subscip, "conflict/maxvarsfac", 0.05) ); /* set limits for the subproblem */ nodelimit = SCIPgetNLPIterations(scip); nodelimit = MAX(sepadata->minnodes, nodelimit); nodelimit = MIN(sepadata->maxnodes, nodelimit); restartnum = 1000; /* check whether there is enough time and memory left */ SCIP_CALL( SCIPgetRealParam(scip, "limits/time", &timelimit) ); if( !SCIPisInfinity(scip, timelimit) ) timelimit -= SCIPgetSolvingTime(scip); SCIP_CALL( SCIPgetRealParam(scip, "limits/memory", &memorylimit) ); if( !SCIPisInfinity(scip, memorylimit) ) memorylimit -= SCIPgetMemUsed(scip)/1048576.0; if( timelimit <= 0.0 || memorylimit <= 0.0 ) goto TERMINATE; SCIP_CALL( SCIPsetLongintParam(subscip, "limits/nodes", nodelimit/5) ); SCIP_CALL( SCIPsetRealParam(subscip, "limits/time", timelimit) ); SCIP_CALL( SCIPsetRealParam(subscip, "limits/memory", memorylimit) ); SCIP_CALL( SCIPsetIntParam(subscip, "limits/restarts", 0) ); SCIP_CALL( SCIPsetIntParam(subscip, "conflict/restartnum", restartnum) ); /* forbid recursive call of heuristics and separators solving subMIPs */ SCIP_CALL( SCIPsetSubscipsOff(subscip, TRUE) ); /* disable cutting plane separation */ SCIP_CALL( SCIPsetSeparating(subscip, SCIP_PARAMSETTING_OFF, TRUE) ); /* disable expensive presolving */ SCIP_CALL( SCIPsetPresolving(subscip, SCIP_PARAMSETTING_FAST, TRUE) ); /* do not abort subproblem on CTRL-C */ SCIP_CALL( SCIPsetBoolParam(subscip, "misc/catchctrlc", FALSE) ); #ifndef SCIP_DEBUG /* disable output to console */ SCIP_CALL( SCIPsetIntParam(subscip, "display/verblevel", 0) ); #endif /* add an objective cutoff */ SCIP_CALL( SCIPsetObjlimit(subscip, SCIPgetUpperbound(scip)) ); /* create the variable mapping hash map */ SCIP_CALL( SCIPhashmapCreate(&varmapbw, SCIPblkmem(scip), SCIPcalcHashtableSize(5 * nvars)) ); /* store reversing mapping of variables */ SCIP_CALL( SCIPtransformProb(subscip) ); for( i = 0; i < nvars; ++i) { SCIP_CALL( SCIPhashmapInsert(varmapbw, SCIPvarGetTransVar(subvars[i]), vars[i]) ); } /** allocate memory for constraints storage. Each constraint that will be created from now on will be a conflict. * Therefore, we need to remember oldnconss to get the conflicts from the FD search. */ nconshdlrs = 4; SCIP_CALL( SCIPallocBufferArray(scip, &conshdlrs, nconshdlrs) ); SCIP_CALL( SCIPallocBufferArray(scip, &oldnconss, nconshdlrs) ); /* store number of constraints before rapid learning search */ conshdlrs[0] = SCIPfindConshdlr(subscip, "bounddisjunction"); conshdlrs[1] = SCIPfindConshdlr(subscip, "setppc"); conshdlrs[2] = SCIPfindConshdlr(subscip, "linear"); conshdlrs[3] = SCIPfindConshdlr(subscip, "logicor"); /* redundant constraints might be eliminated in presolving */ SCIP_CALL( SCIPpresolve(subscip)); for( i = 0; i < nconshdlrs; ++i) { if( conshdlrs[i] != NULL ) oldnconss[i] = SCIPconshdlrGetNConss(conshdlrs[i]); } nfixedvars = SCIPgetNFixedVars(scip); /* solve the subproblem */ retcode = SCIPsolve(subscip); /* Errors in solving the subproblem should not kill the overall solving process * Hence, the return code is caught and a warning is printed, only in debug mode, SCIP will stop. */ if( retcode != SCIP_OKAY ) { #ifndef NDEBUG SCIP_CALL( retcode ); #endif SCIPwarningMessage("Error while solving subproblem in rapid learning separator; sub-SCIP terminated with code <%d>\n",retcode); } /* abort solving, if limit of applied conflicts is reached */ if( SCIPgetNConflictConssApplied(subscip) >= restartnum ) { SCIPdebugMessage("finish after %lld successful conflict calls.\n", SCIPgetNConflictConssApplied(subscip)); } /* if the first 20% of the solution process were successful, proceed */ else if( (sepadata->applyprimalsol && SCIPgetNSols(subscip) > 0 && SCIPisFeasLT(scip, SCIPgetUpperbound(subscip), SCIPgetUpperbound(scip) ) ) || (sepadata->applybdchgs && SCIPgetNFixedVars(subscip) > nfixedvars) || (sepadata->applyconflicts && SCIPgetNConflictConssApplied(subscip) > 0) ) { SCIPdebugMessage("proceed solving after the first 20%% of the solution process, since:\n"); if( SCIPgetNSols(subscip) > 0 && SCIPisFeasLE(scip, SCIPgetUpperbound(subscip), SCIPgetUpperbound(scip) ) ) { SCIPdebugMessage(" - there was a better solution (%f < %f)\n",SCIPgetUpperbound(subscip), SCIPgetUpperbound(scip)); } if( SCIPgetNFixedVars(subscip) > nfixedvars ) { SCIPdebugMessage(" - there were %d variables fixed\n", SCIPgetNFixedVars(scip)-nfixedvars ); } if( SCIPgetNConflictConssFound(subscip) > 0 ) { SCIPdebugMessage(" - there were %lld conflict constraints created\n", SCIPgetNConflictConssApplied(subscip)); } /* set node limit to 100% */ SCIP_CALL( SCIPsetLongintParam(subscip, "limits/nodes", nodelimit) ); /* solve the subproblem */ retcode = SCIPsolve(subscip); /* Errors in solving the subproblem should not kill the overall solving process * Hence, the return code is caught and a warning is printed, only in debug mode, SCIP will stop. */ if( retcode != SCIP_OKAY ) { #ifndef NDEBUG SCIP_CALL( retcode ); #endif SCIPwarningMessage("Error while solving subproblem in rapid learning separator; sub-SCIP terminated with code <%d>\n",retcode); } } else { SCIPdebugMessage("do not proceed solving after the first 20%% of the solution process.\n"); } #ifdef SCIP_DEBUG SCIP_CALL( SCIPprintStatistics(subscip, NULL) ); #endif disabledualreductions = FALSE; /* check, whether a solution was found */ if( sepadata->applyprimalsol && SCIPgetNSols(subscip) > 0 && SCIPfindHeur(scip, "trysol") != NULL ) { SCIP_HEUR* heurtrysol; SCIP_SOL** subsols; int nsubsols; /* check, whether a solution was found; * due to numerics, it might happen that not all solutions are feasible -> try all solutions until was declared to be feasible */ nsubsols = SCIPgetNSols(subscip); subsols = SCIPgetSols(subscip); soladded = FALSE; heurtrysol = SCIPfindHeur(scip, "trysol"); /* sequentially add solutions to trysol heuristic */ for( i = 0; i < nsubsols && !soladded; ++i ) { SCIPdebugMessage("Try to create new solution by copying subscip solution.\n"); SCIP_CALL( createNewSol(scip, subscip, subvars, heurtrysol, subsols[i], &soladded) ); } if( !soladded || !SCIPisEQ(scip, SCIPgetSolOrigObj(subscip, subsols[i-1]), SCIPgetSolOrigObj(subscip, subsols[0])) ) disabledualreductions = TRUE; } /* if the sub problem was solved completely, we update the dual bound */ dualboundchg = FALSE; if( sepadata->applysolved && !disabledualreductions && (SCIPgetStatus(subscip) == SCIP_STATUS_OPTIMAL || SCIPgetStatus(subscip) == SCIP_STATUS_INFEASIBLE) ) { /* we need to multiply the dualbound with the scaling factor and add the offset, * because this information has been disregarded in the sub-SCIP */ SCIPdebugMessage("Update old dualbound %g to new dualbound %g.\n", SCIPgetDualbound(scip), SCIPgetTransObjscale(scip) * SCIPgetDualbound(subscip) + SCIPgetTransObjoffset(scip)); SCIP_CALL( SCIPupdateLocalDualbound(scip, SCIPgetDualbound(subscip) * SCIPgetTransObjscale(scip) + SCIPgetTransObjoffset(scip)) ); dualboundchg = TRUE; } /* check, whether conflicts were created */ nconflicts = 0; if( sepadata->applyconflicts && !disabledualreductions && SCIPgetNConflictConssApplied(subscip) > 0 ) { SCIP_HASHMAP* consmap; int hashtablesize; assert(SCIPgetNConflictConssApplied(subscip) < (SCIP_Longint) INT_MAX); hashtablesize = (int) SCIPgetNConflictConssApplied(subscip); assert(hashtablesize < INT_MAX/5); hashtablesize *= 5; /* create the variable mapping hash map */ SCIP_CALL( SCIPhashmapCreate(&consmap, SCIPblkmem(scip), SCIPcalcHashtableSize(hashtablesize)) ); /* loop over all constraint handlers that might contain conflict constraints */ for( i = 0; i < nconshdlrs; ++i) { /* copy constraints that have been created in FD run */ if( conshdlrs[i] != NULL && SCIPconshdlrGetNConss(conshdlrs[i]) > oldnconss[i] ) { SCIP_CONS** conss; int c; int nconss; nconss = SCIPconshdlrGetNConss(conshdlrs[i]); conss = SCIPconshdlrGetConss(conshdlrs[i]); /* loop over all constraints that have been added in sub-SCIP run, these are the conflicts */ for( c = oldnconss[i]; c < nconss; ++c) { SCIP_CONS* cons; SCIP_CONS* conscopy; cons = conss[c]; assert(cons != NULL); success = FALSE; SCIP_CALL( SCIPgetConsCopy(subscip, scip, cons, &conscopy, conshdlrs[i], varmapbw, consmap, NULL, SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), SCIPconsIsEnforced(cons), SCIPconsIsChecked(cons), SCIPconsIsPropagated(cons), TRUE, FALSE, SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons), FALSE, TRUE, &success) ); if( success ) { nconflicts++; SCIP_CALL( SCIPaddCons(scip, conscopy) ); SCIP_CALL( SCIPreleaseCons(scip, &conscopy) ); } else { SCIPdebugMessage("failed to copy conflict constraint %s back to original SCIP\n", SCIPconsGetName(cons)); } } } } SCIPhashmapFree(&consmap); } /* check, whether tighter global bounds were detected */ nbdchgs = 0; if( sepadata->applybdchgs && !disabledualreductions ) for( i = 0; i < nvars; ++i ) { SCIP_Bool infeasible; SCIP_Bool tightened; assert(SCIPisLE(scip, SCIPvarGetLbGlobal(vars[i]), SCIPvarGetLbGlobal(subvars[i]))); assert(SCIPisLE(scip, SCIPvarGetLbGlobal(subvars[i]), SCIPvarGetUbGlobal(subvars[i]))); assert(SCIPisLE(scip, SCIPvarGetUbGlobal(subvars[i]), SCIPvarGetUbGlobal(vars[i]))); /* update the bounds of the original SCIP, if a better bound was proven in the sub-SCIP */ SCIP_CALL( SCIPtightenVarUb(scip, vars[i], SCIPvarGetUbGlobal(subvars[i]), FALSE, &infeasible, &tightened) ); if( tightened ) nbdchgs++; SCIP_CALL( SCIPtightenVarLb(scip, vars[i], SCIPvarGetLbGlobal(subvars[i]), FALSE, &infeasible, &tightened) ); if( tightened ) nbdchgs++; } n1startinfers = 0; n2startinfers = 0; /* install start values for inference branching */ if( sepadata->applyinfervals && (!sepadata->reducedinfer || soladded || nbdchgs+nconflicts > 0) ) { for( i = 0; i < nvars; ++i ) { SCIP_Real downinfer; SCIP_Real upinfer; SCIP_Real downvsids; SCIP_Real upvsids; SCIP_Real downconflen; SCIP_Real upconflen; /* copy downwards branching statistics */ downvsids = SCIPgetVarVSIDS(subscip, subvars[i], SCIP_BRANCHDIR_DOWNWARDS); downconflen = SCIPgetVarAvgConflictlength(subscip, subvars[i], SCIP_BRANCHDIR_DOWNWARDS); downinfer = SCIPgetVarAvgInferences(subscip, subvars[i], SCIP_BRANCHDIR_DOWNWARDS); /* copy upwards branching statistics */ upvsids = SCIPgetVarVSIDS(subscip, subvars[i], SCIP_BRANCHDIR_UPWARDS); upconflen = SCIPgetVarAvgConflictlength(subscip, subvars[i], SCIP_BRANCHDIR_UPWARDS); upinfer = SCIPgetVarAvgInferences(subscip, subvars[i], SCIP_BRANCHDIR_UPWARDS); /* memorize statistics */ if( downinfer+downconflen+downvsids > 0.0 || upinfer+upconflen+upvsids != 0 ) n1startinfers++; if( downinfer+downconflen+downvsids > 0.0 && upinfer+upconflen+upvsids != 0 ) n2startinfers++; SCIP_CALL( SCIPinitVarBranchStats(scip, vars[i], 0.0, 0.0, downvsids, upvsids, downconflen, upconflen, downinfer, upinfer, 0.0, 0.0) ); } } SCIPdebugPrintf("XXX Rapidlearning added %d conflicts, changed %d bounds, %s primal solution, %s dual bound improvement.\n", nconflicts, nbdchgs, soladded ? "found" : "no", dualboundchg ? "found" : "no"); SCIPdebugPrintf("YYY Infervalues initialized on one side: %5.2f %% of variables, %5.2f %% on both sides\n", 100.0 * n1startinfers/(SCIP_Real)nvars, 100.0 * n2startinfers/(SCIP_Real)nvars); /* change result pointer */ if( nconflicts > 0 || dualboundchg ) *result = SCIP_CONSADDED; else if( nbdchgs > 0 ) *result = SCIP_REDUCEDDOM; /* free local data */ SCIPfreeBufferArray(scip, &oldnconss); SCIPfreeBufferArray(scip, &conshdlrs); SCIPhashmapFree(&varmapbw); TERMINATE: /* free subproblem */ SCIPfreeBufferArray(scip, &subvars); SCIP_CALL( SCIPfree(&subscip) ); return SCIP_OKAY; }
/** searches and adds implied bound cuts that are violated by the given solution value array */ static SCIP_RETCODE separateCuts( SCIP* scip, /**< SCIP data structure */ SCIP_SEPA* sepa, /**< separator */ SCIP_SOL* sol, /**< the solution that should be separated, or NULL for LP solution */ SCIP_Real* solvals, /**< array with solution values of all problem variables */ SCIP_VAR** fracvars, /**< array of fractional variables */ SCIP_Real* fracvals, /**< solution values of fractional variables */ int nfracs, /**< number of fractional variables */ SCIP_Bool* cutoff, /**< whether a cutoff has been detected */ int* ncuts /**< pointer to store the number of generated cuts */ ) { SCIP_CLIQUE** cliques; SCIP_SEPADATA* sepadata; int ncliques; int i; assert(solvals != NULL); assert(fracvars != NULL || nfracs == 0); assert(fracvals != NULL || nfracs == 0); assert(cutoff != NULL); assert(ncuts != NULL); *cutoff = FALSE; *ncuts = 0; sepadata = SCIPsepaGetData(sepa); assert(sepadata != NULL); SCIPdebugMessage("searching for implied bound cuts\n"); /* search binary variables for violated implications */ for( i = 0; i < nfracs; i++ ) { SCIP_BOUNDTYPE* impltypes; SCIP_Real* implbounds; SCIP_VAR** implvars; int nimpl; int j; assert(fracvars != NULL); assert(fracvals != NULL); /* only process binary variables */ if( SCIPvarGetType(fracvars[i]) != SCIP_VARTYPE_BINARY ) continue; /* get implications of x == 1 */ nimpl = SCIPvarGetNImpls(fracvars[i], TRUE); implvars = SCIPvarGetImplVars(fracvars[i], TRUE); impltypes = SCIPvarGetImplTypes(fracvars[i], TRUE); implbounds = SCIPvarGetImplBounds(fracvars[i], TRUE); /*debugMessage("%d implications for <%s>[%g] == 1\n", nimpl, SCIPvarGetName(fracvars[i]), fracvals[i]);*/ /* try to add cuts for implications of x == 1 * x == 1 -> y <= p: y <= ub + x * (p - ub) <==> y + (ub - p) * x <= ub * x == 1 -> y >= p: y >= lb + x * (p - lb) <==> -y + (p - lb) * x <= -lb * with lb (ub) global lower (upper) bound of y */ for( j = 0; j < nimpl; j++ ) { SCIP_Real solval; assert(implvars != NULL); assert(impltypes != NULL); assert(implbounds != NULL); /* consider only implications with active implvar */ if( SCIPvarGetProbindex(implvars[j]) < 0 ) continue; solval = solvals[SCIPvarGetProbindex(implvars[j])]; if( impltypes[j] == SCIP_BOUNDTYPE_UPPER ) { SCIP_Real ub; /* implication x == 1 -> y <= p */ ub = SCIPvarGetUbGlobal(implvars[j]); /* consider only nonredundant and numerical harmless implications */ if( SCIPisLE(scip, implbounds[j], ub) && (ub - implbounds[j]) * SCIPfeastol(scip) <= RELCUTCOEFMAXRANGE ) { /* add cut if violated */ SCIP_CALL( addCut(scip, sepa, sol, 1.0, implvars[j], solval, (ub - implbounds[j]), fracvars[i], fracvals[i], ub, cutoff, ncuts) ); if ( *cutoff ) return SCIP_OKAY; } } else { SCIP_Real lb; /* implication x == 1 -> y >= p */ lb = SCIPvarGetLbGlobal(implvars[j]); assert(impltypes[j] == SCIP_BOUNDTYPE_LOWER); /* consider only nonredundant and numerical harmless implications */ if( SCIPisGE(scip, implbounds[j], lb) && (implbounds[j] - lb) * SCIPfeastol(scip) <= RELCUTCOEFMAXRANGE ) { /* add cut if violated */ SCIP_CALL( addCut(scip, sepa, sol, -1.0, implvars[j], solval, (implbounds[j] - lb), fracvars[i], fracvals[i], -lb, cutoff, ncuts) ); if ( *cutoff ) return SCIP_OKAY; } } } /* get implications of x == 0 */ nimpl = SCIPvarGetNImpls(fracvars[i], FALSE); implvars = SCIPvarGetImplVars(fracvars[i], FALSE); impltypes = SCIPvarGetImplTypes(fracvars[i], FALSE); implbounds = SCIPvarGetImplBounds(fracvars[i], FALSE); /*debugMessage("%d implications for <%s>[%g] == 0\n", nimpl, SCIPvarGetName(fracvars[i]), fracvals[i]);*/ /* try to add cuts for implications of x == 0 * x == 0 -> y <= p: y <= p + x * (ub - p) <==> y + (p - ub) * x <= p * x == 0 -> y >= p: y >= p + x * (lb - p) <==> -y + (lb - p) * x <= -p * with lb (ub) global lower (upper) bound of y */ for( j = 0; j < nimpl; j++ ) { SCIP_Real solval; /* consider only implications with active implvar */ if( SCIPvarGetProbindex(implvars[j]) < 0 ) continue; solval = solvals[SCIPvarGetProbindex(implvars[j])]; if( impltypes[j] == SCIP_BOUNDTYPE_UPPER ) { SCIP_Real ub; /* implication x == 0 -> y <= p */ ub = SCIPvarGetUbGlobal(implvars[j]); /* consider only nonredundant and numerical harmless implications */ if( SCIPisLE(scip, implbounds[j], ub) && (ub - implbounds[j]) * SCIPfeastol(scip) < RELCUTCOEFMAXRANGE ) { /* add cut if violated */ SCIP_CALL( addCut(scip, sepa, sol, 1.0, implvars[j], solval, (implbounds[j] - ub), fracvars[i], fracvals[i], implbounds[j], cutoff, ncuts) ); if ( *cutoff ) return SCIP_OKAY; } } else { SCIP_Real lb; /* implication x == 0 -> y >= p */ lb = SCIPvarGetLbGlobal(implvars[j]); assert(impltypes[j] == SCIP_BOUNDTYPE_LOWER); /* consider only nonredundant and numerical harmless implications */ if( SCIPisGE(scip, implbounds[j], lb) && (implbounds[j] - lb) * SCIPfeastol(scip) < RELCUTCOEFMAXRANGE ) { /* add cut if violated */ SCIP_CALL( addCut(scip, sepa, sol, -1.0, implvars[j], solval, (lb - implbounds[j]), fracvars[i], fracvals[i], -implbounds[j], cutoff, ncuts) ); if ( *cutoff ) return SCIP_OKAY; } } } } /* stop separation here if cliques should not be separated */ if( ! sepadata->usetwosizecliques ) return SCIP_OKAY; /* prepare clean clique data */ SCIP_CALL( SCIPcleanupCliques(scip, cutoff) ); if( *cutoff ) return SCIP_OKAY; cliques = SCIPgetCliques(scip); ncliques = SCIPgetNCliques(scip); /* loop over cliques of size 2 which are essentially implications and add cuts if they are violated */ for( i = 0; i < ncliques; ++i ) { SCIP_CLIQUE* clique; SCIP_VAR** clqvars; SCIP_Bool* clqvals; SCIP_Real rhs; clique = cliques[i]; /* only consider inequality cliques of size 2 */ if( SCIPcliqueGetNVars(clique) != 2 || SCIPcliqueIsEquation(clique) ) continue; /* get variables and values of the clique */ clqvars = SCIPcliqueGetVars(clique); clqvals = SCIPcliqueGetValues(clique); /* clique variables should never be equal after clean up */ assert(clqvars[0] != clqvars[1]); /* calculate right hand side of clique inequality, which is initially 1 and decreased by 1 for every occurence of * a negated variable in the clique */ rhs = 1.0; if( ! clqvals[0] ) rhs -= 1.0; if( ! clqvals[1] ) rhs -= 1.0; /* Basic clique inequality is * * cx * x + (1-cx) (1-x) + cy * y + (1-cy) * (1-y) <= 1, * * where x and y are the two binary variables in the clique and cx and cy are their clique values, where a * clique value of 0 means that the negation of the variable should be part of the inequality. * Hence, exactly one of the two possible terms for x and y has a nonzero coefficient */ SCIP_CALL( addCut(scip, sepa, sol, clqvals[0] ? 1.0 : -1.0, clqvars[0], SCIPgetSolVal(scip, sol, clqvars[0]), clqvals[1] ? 1.0 : -1.0, clqvars[1], SCIPgetSolVal(scip, sol, clqvars[1]), rhs, cutoff, ncuts) ); /* terminate if cutoff was found */ if( *cutoff ) return SCIP_OKAY; } return SCIP_OKAY; }
/** execution method of primal heuristic */ static SCIP_DECL_HEUREXEC(heurExecRounding) /*lint --e{715}*/ { /*lint --e{715}*/ SCIP_HEURDATA* heurdata; SCIP_SOL* sol; SCIP_VAR** lpcands; SCIP_Real* lpcandssol; SCIP_ROW** lprows; SCIP_Real* activities; SCIP_ROW** violrows; int* violrowpos; SCIP_Real obj; SCIP_Real bestroundval; SCIP_Real minobj; int nlpcands; int nlprows; int nfrac; int nviolrows; int c; int r; SCIP_Longint nlps; SCIP_Longint ncalls; SCIP_Longint nsolsfound; SCIP_Longint nnodes; assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0); assert(scip != NULL); assert(result != NULL); assert(SCIPhasCurrentNodeLP(scip)); *result = SCIP_DIDNOTRUN; /* 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; /* get heuristic data */ heurdata = SCIPheurGetData(heur); assert(heurdata != NULL); /* don't call heuristic, if we have already processed the current LP solution */ nlps = SCIPgetNLPs(scip); if( nlps == heurdata->lastlp ) return SCIP_OKAY; heurdata->lastlp = nlps; /* don't call heuristic, if it was not successful enough in the past */ ncalls = SCIPheurGetNCalls(heur); nsolsfound = 10*SCIPheurGetNBestSolsFound(heur) + SCIPheurGetNSolsFound(heur); nnodes = SCIPgetNNodes(scip); if( nnodes % ((ncalls/heurdata->successfactor)/(nsolsfound+1)+1) != 0 ) return SCIP_OKAY; /* get fractional variables, that should be integral */ SCIP_CALL( SCIPgetLPBranchCands(scip, &lpcands, &lpcandssol, NULL, &nlpcands, NULL, NULL) ); nfrac = nlpcands; /* only call heuristic, if LP solution is fractional */ if( nfrac == 0 ) return SCIP_OKAY; *result = SCIP_DIDNOTFIND; /* get LP rows */ SCIP_CALL( SCIPgetLPRowsData(scip, &lprows, &nlprows) ); SCIPdebugMessage("executing rounding heuristic: %d LP rows, %d fractionals\n", nlprows, nfrac); /* get memory for activities, violated rows, and row violation positions */ SCIP_CALL( SCIPallocBufferArray(scip, &activities, nlprows) ); SCIP_CALL( SCIPallocBufferArray(scip, &violrows, nlprows) ); SCIP_CALL( SCIPallocBufferArray(scip, &violrowpos, nlprows) ); /* get the activities for all globally valid rows; * the rows should be feasible, but due to numerical inaccuracies in the LP solver, they can be violated */ nviolrows = 0; for( r = 0; r < nlprows; ++r ) { SCIP_ROW* row; row = lprows[r]; assert(SCIProwGetLPPos(row) == r); if( !SCIProwIsLocal(row) ) { activities[r] = SCIPgetRowActivity(scip, row); if( SCIPisFeasLT(scip, activities[r], SCIProwGetLhs(row)) || SCIPisFeasGT(scip, activities[r], SCIProwGetRhs(row)) ) { violrows[nviolrows] = row; violrowpos[r] = nviolrows; nviolrows++; } else violrowpos[r] = -1; } } /* get the working solution from heuristic's local data */ sol = heurdata->sol; assert(sol != NULL); /* copy the current LP solution to the working solution */ SCIP_CALL( SCIPlinkLPSol(scip, sol) ); /* calculate the minimal objective value possible after rounding fractional variables */ minobj = SCIPgetSolTransObj(scip, sol); assert(minobj < SCIPgetCutoffbound(scip)); for( c = 0; c < nlpcands; ++c ) { obj = SCIPvarGetObj(lpcands[c]); bestroundval = obj > 0.0 ? SCIPfeasFloor(scip, lpcandssol[c]) : SCIPfeasCeil(scip, lpcandssol[c]); minobj += obj * (bestroundval - lpcandssol[c]); } /* try to round remaining variables in order to become/stay feasible */ while( nfrac > 0 ) { SCIP_VAR* roundvar; SCIP_Real oldsolval; SCIP_Real newsolval; SCIPdebugMessage("rounding heuristic: nfrac=%d, nviolrows=%d, obj=%g (best possible obj: %g)\n", nfrac, nviolrows, SCIPgetSolOrigObj(scip, sol), SCIPretransformObj(scip, minobj)); /* minobj < SCIPgetCutoffbound(scip) should be true, otherwise the rounding variable selection * should have returned NULL. Due to possible cancellation we use SCIPisLE. */ assert( SCIPisLE(scip, minobj, SCIPgetCutoffbound(scip)) ); /* choose next variable to process: * - if a violated row exists, round a variable decreasing the violation, that has least impact on other rows * - otherwise, round a variable, that has strongest devastating impact on rows in opposite direction */ if( nviolrows > 0 ) { SCIP_ROW* row; int rowpos; row = violrows[nviolrows-1]; rowpos = SCIProwGetLPPos(row); assert(0 <= rowpos && rowpos < nlprows); assert(violrowpos[rowpos] == nviolrows-1); SCIPdebugMessage("rounding heuristic: try to fix violated row <%s>: %g <= %g <= %g\n", SCIProwGetName(row), SCIProwGetLhs(row), activities[rowpos], SCIProwGetRhs(row)); if( SCIPisFeasLT(scip, activities[rowpos], SCIProwGetLhs(row)) ) { /* lhs is violated: select a variable rounding, that increases the activity */ SCIP_CALL( selectIncreaseRounding(scip, sol, minobj, row, &roundvar, &oldsolval, &newsolval) ); } else { assert(SCIPisFeasGT(scip, activities[rowpos], SCIProwGetRhs(row))); /* rhs is violated: select a variable rounding, that decreases the activity */ SCIP_CALL( selectDecreaseRounding(scip, sol, minobj, row, &roundvar, &oldsolval, &newsolval) ); } } else { SCIPdebugMessage("rounding heuristic: search rounding variable and try to stay feasible\n"); SCIP_CALL( selectEssentialRounding(scip, sol, minobj, lpcands, nlpcands, &roundvar, &oldsolval, &newsolval) ); } /* check, whether rounding was possible */ if( roundvar == NULL ) { SCIPdebugMessage("rounding heuristic: -> didn't find a rounding variable\n"); break; } SCIPdebugMessage("rounding heuristic: -> round var <%s>, oldval=%g, newval=%g, obj=%g\n", SCIPvarGetName(roundvar), oldsolval, newsolval, SCIPvarGetObj(roundvar)); /* update row activities of globally valid rows */ SCIP_CALL( updateActivities(scip, activities, violrows, violrowpos, &nviolrows, nlprows, roundvar, oldsolval, newsolval) ); /* store new solution value and decrease fractionality counter */ SCIP_CALL( SCIPsetSolVal(scip, sol, roundvar, newsolval) ); nfrac--; /* update minimal objective value possible after rounding remaining variables */ obj = SCIPvarGetObj(roundvar); if( obj > 0.0 && newsolval > oldsolval ) minobj += obj; else if( obj < 0.0 && newsolval < oldsolval ) minobj -= obj; SCIPdebugMessage("rounding heuristic: -> nfrac=%d, nviolrows=%d, obj=%g (best possible obj: %g)\n", nfrac, nviolrows, SCIPgetSolOrigObj(scip, sol), SCIPretransformObj(scip, minobj)); } /* check, if the new solution is feasible */ if( nfrac == 0 && nviolrows == 0 ) { SCIP_Bool stored; /* check solution for feasibility, and add it to solution store if possible * neither integrality nor feasibility of LP rows has to be checked, because this is already * done in the rounding heuristic itself; however, be better check feasibility of LP rows, * because of numerical problems with activity updating */ SCIP_CALL( SCIPtrySol(scip, sol, FALSE, FALSE, FALSE, TRUE, &stored) ); if( stored ) { #ifdef SCIP_DEBUG SCIPdebugMessage("found feasible rounded solution:\n"); SCIP_CALL( SCIPprintSol(scip, sol, NULL, FALSE) ); #endif *result = SCIP_FOUNDSOL; } } /* free memory buffers */ SCIPfreeBufferArray(scip, &violrowpos); SCIPfreeBufferArray(scip, &violrows); SCIPfreeBufferArray(scip, &activities); return SCIP_OKAY; }