/** performs the all fullstrong branching */ static SCIP_RETCODE branch( SCIP* scip, /**< SCIP data structure */ SCIP_BRANCHRULE* branchrule, /**< branching rule */ SCIP_Bool allowaddcons, /**< should adding constraints be allowed to avoid a branching? */ SCIP_RESULT* result /**< pointer to store the result of the callback method */ ) { SCIP_BRANCHRULEDATA* branchruledata; SCIP_VAR** pseudocands; SCIP_Real bestdown; SCIP_Real bestup; SCIP_Real bestscore; SCIP_Real provedbound; SCIP_Bool exactsolve; SCIP_Bool allcolsinlp; SCIP_Bool bestdownvalid; SCIP_Bool bestupvalid; int npseudocands; int npriopseudocands; int bestpseudocand; #ifndef NDEBUG SCIP_Real cutoffbound; cutoffbound = SCIPgetCutoffbound(scip); #endif assert(branchrule != NULL); assert(strcmp(SCIPbranchruleGetName(branchrule), BRANCHRULE_NAME) == 0); assert(scip != NULL); assert(result != NULL); /* check, if all existing columns are in LP, and thus the strong branching results give lower bounds */ allcolsinlp = SCIPallColsInLP(scip); /* check, if we want to solve the problem exactly, meaning that strong branching information is not useful * for cutting off sub problems and improving lower bounds of children */ exactsolve = SCIPisExactSolve(scip); /* get branching rule data */ branchruledata = SCIPbranchruleGetData(branchrule); assert(branchruledata != NULL); if( branchruledata->skipdown == NULL ) { int nvars; nvars = SCIPgetNVars(scip); assert(branchruledata->skipup == NULL); SCIP_CALL( SCIPallocMemoryArray(scip, &branchruledata->skipdown, nvars) ); SCIP_CALL( SCIPallocMemoryArray(scip, &branchruledata->skipup, nvars) ); BMSclearMemoryArray(branchruledata->skipdown, nvars); BMSclearMemoryArray(branchruledata->skipup, nvars); } /* get all non-fixed variables (not only the fractional ones) */ SCIP_CALL( SCIPgetPseudoBranchCands(scip, &pseudocands, &npseudocands, &npriopseudocands) ); assert(npseudocands > 0); assert(npriopseudocands > 0); SCIP_CALL( SCIPselectVarPseudoStrongBranching(scip, pseudocands, branchruledata->skipdown, branchruledata->skipup, npseudocands, npriopseudocands, allowaddcons, &bestpseudocand, &bestdown, &bestup, &bestscore, &bestdownvalid, &bestupvalid, &provedbound, result) ); if( *result != SCIP_CUTOFF && *result != SCIP_REDUCEDDOM && *result != SCIP_CONSADDED ) { SCIP_NODE* downchild; SCIP_NODE* eqchild; SCIP_NODE* upchild; SCIP_VAR* var; assert(*result == SCIP_DIDNOTRUN); assert(0 <= bestpseudocand && bestpseudocand < npseudocands); assert(SCIPisLT(scip, provedbound, cutoffbound)); var = pseudocands[bestpseudocand]; /* perform the branching */ SCIPdebugMessage(" -> %d candidates, selected candidate %d: variable <%s>[%g,%g] (solval=%g, down=%g, up=%g, score=%g)\n", npseudocands, bestpseudocand, SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var), SCIPvarGetLPSol(var), bestdown, bestup, bestscore); SCIP_CALL( SCIPbranchVarVal(scip, var, SCIPvarGetLPSol(var), &downchild, &eqchild, &upchild) ); /* update the lower bounds in the children */ if( allcolsinlp && !exactsolve ) { if( downchild != NULL ) { SCIP_CALL( SCIPupdateNodeLowerbound(scip, downchild, bestdownvalid ? MAX(bestdown, provedbound) : provedbound) ); SCIPdebugMessage(" -> down child's lowerbound: %g\n", SCIPnodeGetLowerbound(downchild)); } if( eqchild != NULL ) { SCIP_CALL( SCIPupdateNodeLowerbound(scip, eqchild, provedbound) ); SCIPdebugMessage(" -> eq child's lowerbound: %g\n", SCIPnodeGetLowerbound(eqchild)); } if( upchild != NULL ) { SCIP_CALL( SCIPupdateNodeLowerbound(scip, upchild, bestupvalid ? MAX(bestup, provedbound) : provedbound) ); SCIPdebugMessage(" -> up child's lowerbound: %g\n", SCIPnodeGetLowerbound(upchild)); } } *result = SCIP_BRANCHED; } return SCIP_OKAY; }
/** perform randomized rounding of the given solution. Domain propagation is optionally applied after every rounding * step */ static SCIP_RETCODE performRandRounding( SCIP* scip, /**< SCIP main data structure */ SCIP_HEURDATA* heurdata, /**< heuristic data */ SCIP_SOL* sol, /**< solution to round */ SCIP_VAR** cands, /**< candidate variables */ int ncands, /**< number of candidates */ SCIP_Bool propagate, /**< should the rounding be propagated? */ SCIP_RESULT* result /**< pointer to store the result of the heuristic call */ ) { int c; SCIP_Bool stored; SCIP_VAR** permutedcands; SCIP_Bool cutoff; assert(heurdata != NULL); /* start probing tree before rounding begins */ if( propagate ) { SCIP_CALL( SCIPstartProbing(scip) ); SCIPenableVarHistory(scip); } /* copy and permute the candidate array */ SCIP_CALL( SCIPduplicateBufferArray(scip, &permutedcands, cands, ncands) ); assert(permutedcands != NULL); SCIPpermuteArray((void **)permutedcands, 0, ncands, &heurdata->randseed); cutoff = FALSE; /* loop over candidates and perform randomized rounding and optionally probing. */ for (c = 0; c < ncands && !cutoff; ++c) { SCIP_VAR* var; SCIP_Real oldsolval; SCIP_Real newsolval; SCIP_Bool mayrounddown; SCIP_Bool mayroundup; SCIP_Longint ndomreds; SCIP_Real lb; SCIP_Real ub; SCIP_Real ceilval; SCIP_Real floorval; /* get next variable from permuted candidate array */ var = permutedcands[c]; oldsolval = SCIPgetSolVal(scip, sol, var); lb = SCIPvarGetLbLocal(var); ub = SCIPvarGetUbLocal(var); assert( ! SCIPisFeasIntegral(scip, oldsolval) ); assert( SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN ); mayrounddown = SCIPvarMayRoundDown(var); mayroundup = SCIPvarMayRoundUp(var); ceilval = SCIPfeasCeil(scip, oldsolval); floorval = SCIPfeasFloor(scip, oldsolval); SCIPdebugMessage("rand rounding heuristic: var <%s>, val=%g, rounddown=%u, roundup=%u\n", SCIPvarGetName(var), oldsolval, mayrounddown, mayroundup); /* abort if rounded ceil and floor value lie outside the variable domain. Otherwise, check if * bounds allow only one rounding direction, anyway */ if( lb > ceilval + 0.5 || ub < floorval - 0.5 ) { cutoff = TRUE; break; } else if( SCIPisFeasEQ(scip, lb, ceilval) ) { /* only rounding up possible */ assert(SCIPisFeasGE(scip, ub, ceilval)); newsolval = ceilval; } else if( SCIPisFeasEQ(scip, ub, floorval) ) { /* only rounding down possible */ assert(SCIPisFeasLE(scip,lb, floorval)); newsolval = floorval; } else if( !heurdata->usesimplerounding || !(mayroundup || mayrounddown) ) { /* the standard randomized rounding */ SCIP_Real randnumber; randnumber = SCIPgetRandomReal(0.0, 1.0, &heurdata->randseed); if( randnumber <= oldsolval - floorval ) newsolval = ceilval; else newsolval = floorval; } /* choose rounding direction, if possible, or use the only direction guaranteed to be feasible */ else if( mayrounddown && mayroundup ) { /* we can round in both directions: round in objective function direction */ if ( SCIPvarGetObj(var) >= 0.0 ) newsolval = floorval; else newsolval = ceilval; } else if( mayrounddown ) newsolval = floorval; else { assert(mayroundup); newsolval = ceilval; } assert(SCIPisFeasLE(scip, lb, newsolval)); assert(SCIPisFeasGE(scip, ub, newsolval)); /* if propagation is enabled, fix the candidate variable to its rounded value and propagate the solution */ if( propagate ) { SCIP_Bool lbadjust; SCIP_Bool ubadjust; lbadjust = SCIPisGT(scip, newsolval, lb); ubadjust = SCIPisLT(scip, newsolval, ub); assert( lbadjust || ubadjust || SCIPisFeasEQ(scip, lb, ub)); /* enter a new probing node if the variable was not already fixed before */ if( lbadjust || ubadjust ) { SCIP_RETCODE retcode; if( SCIPisStopped(scip) ) break; retcode = SCIPnewProbingNode(scip); if( retcode == SCIP_MAXDEPTHLEVEL ) break; SCIP_CALL( retcode ); /* tighten the bounds to fix the variable for the probing node */ if( lbadjust ) { SCIP_CALL( SCIPchgVarLbProbing(scip, var, newsolval) ); } if( ubadjust ) { SCIP_CALL( SCIPchgVarUbProbing(scip, var, newsolval) ); } /* call propagation routines for the reduced problem */ SCIP_CALL( SCIPpropagateProbing(scip, heurdata->maxproprounds, &cutoff, &ndomreds) ); } } /* store new solution value */ SCIP_CALL( SCIPsetSolVal(scip, sol, var, newsolval) ); } /* if no cutoff was detected, the solution is a candidate to be checked for feasibility */ if( !cutoff && ! SCIPisStopped(scip) ) { if( SCIPallColsInLP(scip) ) { /* check solution for feasibility, and add it to solution store if possible * neither integrality nor feasibility of LP rows has to be checked, because all fractional * variables were already moved in feasible direction to the next integer */ SCIP_CALL( SCIPtrySol(scip, sol, FALSE, FALSE, FALSE, TRUE, &stored) ); } else { /* if there are variables which are not present in the LP, e.g., for * column generation, we need to check their bounds */ SCIP_CALL( SCIPtrySol(scip, sol, FALSE, TRUE, 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; } } assert( !propagate || SCIPinProbing(scip) ); /* exit probing mode and free locally allocated memory */ if( propagate ) { SCIP_CALL( SCIPendProbing(scip) ); } SCIPfreeBufferArray(scip, &permutedcands); return SCIP_OKAY; }
/** * Selects a variable from a set of candidates by strong branching * * @return \ref SCIP_OKAY is returned if everything worked. Otherwise a suitable error code is passed. See \ref * SCIP_Retcode "SCIP_RETCODE" for a complete list of error codes. * * @note The variables in the lpcands array must have a fractional value in the current LP solution */ SCIP_RETCODE SCIPselectVarPseudoStrongBranching( SCIP* scip, /**< original SCIP data structure */ SCIP_VAR** pseudocands, /**< branching candidates */ SCIP_Bool* skipdown, /**< should down branchings be skipped? */ SCIP_Bool* skipup, /**< should up branchings be skipped? */ int npseudocands, /**< number of branching candidates */ int npriopseudocands, /**< number of priority branching candidates */ SCIP_Bool allowaddcons, /**< is the branching rule allowed to add constraints? */ int* bestpseudocand, /**< best candidate for branching */ SCIP_Real* bestdown, /**< objective value of the down branch for bestcand */ SCIP_Real* bestup, /**< objective value of the up branch for bestcand */ SCIP_Real* bestscore, /**< score for bestcand */ SCIP_Bool* bestdownvalid, /**< is bestdown a valid dual bound for the down branch? */ SCIP_Bool* bestupvalid, /**< is bestup a valid dual bound for the up branch? */ SCIP_Real* provedbound, /**< proved dual bound for current subtree */ SCIP_RESULT* result /**< result pointer */ ) { SCIP_Real lpobjval; SCIP_Bool allcolsinlp; SCIP_Bool exactsolve; #ifndef NDEBUG SCIP_Real cutoffbound; cutoffbound = SCIPgetCutoffbound(scip); #endif assert(scip != NULL); assert(pseudocands != NULL); assert(bestpseudocand != NULL); assert(skipdown != NULL); assert(skipup != NULL); assert(bestdown != NULL); assert(bestup != NULL); assert(bestscore != NULL); assert(bestdownvalid != NULL); assert(bestupvalid != NULL); assert(provedbound != NULL); assert(result != NULL); assert(SCIPgetLPSolstat(scip) == SCIP_LPSOLSTAT_OPTIMAL); /* get current LP objective bound of the local sub problem and global cutoff bound */ lpobjval = SCIPgetLPObjval(scip); /* check, if we want to solve the problem exactly, meaning that strong branching information is not useful * for cutting off sub problems and improving lower bounds of children */ exactsolve = SCIPisExactSolve(scip); /* check, if all existing columns are in LP, and thus the strong branching results give lower bounds */ allcolsinlp = SCIPallColsInLP(scip); /* if only one candidate exists, choose this one without applying strong branching */ *bestpseudocand = 0; *bestdown = lpobjval; *bestup = lpobjval; *bestdownvalid = TRUE; *bestupvalid = TRUE; *bestscore = -SCIPinfinity(scip); *provedbound = lpobjval; if( npseudocands > 1 ) { SCIP_BRANCHRULE* branchrule; SCIP_BRANCHRULEDATA* branchruledata; SCIP_Real solval; SCIP_Real down; SCIP_Real up; SCIP_Real downgain; SCIP_Real upgain; SCIP_Real score; SCIP_Bool integral; SCIP_Bool lperror; SCIP_Bool downvalid; SCIP_Bool upvalid; SCIP_Bool downinf; SCIP_Bool upinf; SCIP_Bool downconflict; SCIP_Bool upconflict; int nsbcalls; int i; int c; branchrule = SCIPfindBranchrule(scip, BRANCHRULE_NAME); assert(branchrule != NULL); /* get branching rule data */ branchruledata = SCIPbranchruleGetData(branchrule); assert(branchruledata != NULL); /* initialize strong branching */ SCIP_CALL( SCIPstartStrongbranch(scip, FALSE) ); /* search the full strong candidate: * cycle through the candidates, starting with the position evaluated in the last run */ nsbcalls = 0; for( i = 0, c = branchruledata->lastcand; i < npseudocands; ++i, ++c ) { c = c % npseudocands; assert(pseudocands[c] != NULL); /* we can only apply strong branching on COLUMN variables that are in the current LP */ if( !SCIPvarIsInLP(pseudocands[c]) ) continue; solval = SCIPvarGetLPSol(pseudocands[c]); integral = SCIPisFeasIntegral(scip, solval); SCIPdebugMessage("applying strong branching on %s variable <%s>[%g,%g] with solution %g\n", integral ? "integral" : "fractional", SCIPvarGetName(pseudocands[c]), SCIPvarGetLbLocal(pseudocands[c]), SCIPvarGetUbLocal(pseudocands[c]), solval); up = -SCIPinfinity(scip); down = -SCIPinfinity(scip); if( integral ) { SCIP_CALL( SCIPgetVarStrongbranchInt(scip, pseudocands[c], INT_MAX, skipdown[c] ? NULL : &down, skipup[c] ? NULL : &up, &downvalid, &upvalid, &downinf, &upinf, &downconflict, &upconflict, &lperror) ); } else { SCIP_CALL( SCIPgetVarStrongbranchFrac(scip, pseudocands[c], INT_MAX, skipdown[c] ? NULL : &down, skipup[c] ? NULL : &up, &downvalid, &upvalid, &downinf, &upinf, &downconflict, &upconflict, &lperror) ); } nsbcalls++; /* display node information line in root node */ if( SCIPgetDepth(scip) == 0 && nsbcalls % 100 == 0 ) { SCIP_CALL( SCIPprintDisplayLine(scip, NULL, SCIP_VERBLEVEL_HIGH, TRUE) ); } /* check for an error in strong branching */ if( lperror ) { SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "(node %"SCIP_LONGINT_FORMAT") error in strong branching call for variable <%s> with solution %g\n", SCIPgetNNodes(scip), SCIPvarGetName(pseudocands[c]), solval); break; } /* evaluate strong branching */ down = MAX(down, lpobjval); up = MAX(up, lpobjval); downgain = down - lpobjval; upgain = up - lpobjval; assert(!allcolsinlp || exactsolve || !downvalid || downinf == SCIPisGE(scip, down, cutoffbound)); assert(!allcolsinlp || exactsolve || !upvalid || upinf == SCIPisGE(scip, up, cutoffbound)); assert(downinf || !downconflict); assert(upinf || !upconflict); /* check if there are infeasible roundings */ if( downinf || upinf ) { assert(allcolsinlp); assert(!exactsolve); /* if for both infeasibilities, a conflict constraint was created, we don't need to fix the variable by hand, * but better wait for the next propagation round to fix them as an inference, and potentially produce a * cutoff that can be analyzed */ if( allowaddcons && downinf == downconflict && upinf == upconflict ) { *result = SCIP_CONSADDED; break; /* terminate initialization loop, because constraint was added */ } else if( downinf && upinf ) { if( integral ) { SCIP_Bool infeasible; SCIP_Bool fixed; /* both bound changes are infeasible: variable can be fixed to its current value */ SCIP_CALL( SCIPfixVar(scip, pseudocands[c], solval, &infeasible, &fixed) ); assert(!infeasible); assert(fixed); *result = SCIP_REDUCEDDOM; SCIPdebugMessage(" -> integral variable <%s> is infeasible in both directions\n", SCIPvarGetName(pseudocands[c])); break; /* terminate initialization loop, because LP was changed */ } else { /* both roundings are infeasible: the node is infeasible */ *result = SCIP_CUTOFF; SCIPdebugMessage(" -> fractional variable <%s> is infeasible in both directions\n", SCIPvarGetName(pseudocands[c])); break; /* terminate initialization loop, because node is infeasible */ } } else if( downinf ) { SCIP_Real newlb; /* downwards rounding is infeasible -> change lower bound of variable to upward rounding */ newlb = SCIPfeasCeil(scip, solval); if( SCIPvarGetLbLocal(pseudocands[c]) < newlb - 0.5 ) { SCIP_CALL( SCIPchgVarLb(scip, pseudocands[c], newlb) ); *result = SCIP_REDUCEDDOM; SCIPdebugMessage(" -> variable <%s> is infeasible in downward branch\n", SCIPvarGetName(pseudocands[c])); break; /* terminate initialization loop, because LP was changed */ } } else { SCIP_Real newub; /* upwards rounding is infeasible -> change upper bound of variable to downward rounding */ assert(upinf); newub = SCIPfeasFloor(scip, solval); if( SCIPvarGetUbLocal(pseudocands[c]) > newub + 0.5 ) { SCIP_CALL( SCIPchgVarUb(scip, pseudocands[c], newub) ); *result = SCIP_REDUCEDDOM; SCIPdebugMessage(" -> variable <%s> is infeasible in upward branch\n", SCIPvarGetName(pseudocands[c])); break; /* terminate initialization loop, because LP was changed */ } } } else if( allcolsinlp && !exactsolve && downvalid && upvalid ) { SCIP_Real minbound; /* the minimal lower bound of both children is a proved lower bound of the current subtree */ minbound = MIN(down, up); *provedbound = MAX(*provedbound, minbound); } /* check for a better score, if we are within the maximum priority candidates */ if( c < npriopseudocands ) { if( integral ) { if( skipdown[c] ) { downgain = 0.0; score = SCIPgetBranchScore(scip, pseudocands[c], downgain, upgain); } else if( skipup[c] ) { upgain = 0.0; score = SCIPgetBranchScore(scip, pseudocands[c], downgain, upgain); } else { SCIP_Real gains[3]; gains[0] = downgain; gains[1] = 0.0; gains[2] = upgain; score = SCIPgetBranchScoreMultiple(scip, pseudocands[c], 3, gains); } } else score = SCIPgetBranchScore(scip, pseudocands[c], downgain, upgain); if( score > *bestscore ) { *bestpseudocand = c; *bestdown = down; *bestup = up; *bestdownvalid = downvalid; *bestupvalid = upvalid; *bestscore = score; } } else score = 0.0; /* update pseudo cost values */ if( !downinf ) { SCIP_CALL( SCIPupdateVarPseudocost(scip, pseudocands[c], solval-SCIPfeasCeil(scip, solval-1.0), downgain, 1.0) ); } if( !upinf ) { SCIP_CALL( SCIPupdateVarPseudocost(scip, pseudocands[c], solval-SCIPfeasFloor(scip, solval+1.0), upgain, 1.0) ); } SCIPdebugMessage(" -> var <%s> (solval=%g, downgain=%g, upgain=%g, score=%g) -- best: <%s> (%g)\n", SCIPvarGetName(pseudocands[c]), solval, downgain, upgain, score, SCIPvarGetName(pseudocands[*bestpseudocand]), *bestscore); } /* remember last evaluated candidate */ branchruledata->lastcand = c; /* end strong branching */ SCIP_CALL( SCIPendStrongbranch(scip) ); } return SCIP_OKAY; }