/** main procedure of the zeroobj heuristic, creates and solves a sub-SCIP */ SCIP_RETCODE SCIPapplyZeroobj( SCIP* scip, /**< original SCIP data structure */ SCIP_HEUR* heur, /**< heuristic data structure */ SCIP_RESULT* result, /**< result data structure */ SCIP_Real minimprove, /**< factor by which zeroobj should at least improve the incumbent */ SCIP_Longint nnodes /**< node limit for the subproblem */ ) { SCIP* subscip; /* the subproblem created by zeroobj */ SCIP_HASHMAP* varmapfw; /* mapping of SCIP variables to sub-SCIP variables */ SCIP_VAR** vars; /* original problem's variables */ SCIP_VAR** subvars; /* subproblem's variables */ SCIP_HEURDATA* heurdata; /* heuristic's private data structure */ SCIP_EVENTHDLR* eventhdlr; /* event handler for LP events */ SCIP_Real cutoff; /* objective cutoff for the subproblem */ SCIP_Real timelimit; /* time limit for zeroobj subproblem */ SCIP_Real memorylimit; /* memory limit for zeroobj subproblem */ SCIP_Real large; int nvars; /* number of original problem's variables */ int i; SCIP_Bool success; SCIP_Bool valid; SCIP_RETCODE retcode; SCIP_SOL** subsols; int nsubsols; assert(scip != NULL); assert(heur != NULL); assert(result != NULL); assert(nnodes >= 0); assert(0.0 <= minimprove && minimprove <= 1.0); *result = SCIP_DIDNOTRUN; /* only call heuristic once at the root */ if( SCIPgetDepth(scip) <= 0 && SCIPheurGetNCalls(heur) > 0 ) return SCIP_OKAY; /* get heuristic data */ heurdata = SCIPheurGetData(heur); assert(heurdata != NULL); /* only call the heuristic if we do not have an incumbent */ if( SCIPgetNSolsFound(scip) > 0 && heurdata->onlywithoutsol ) return SCIP_OKAY; /* check whether there is enough time and memory left */ timelimit = 0.0; memorylimit = 0.0; SCIP_CALL( SCIPgetRealParam(scip, "limits/time", &timelimit) ); if( !SCIPisInfinity(scip, timelimit) ) timelimit -= SCIPgetSolvingTime(scip); SCIP_CALL( SCIPgetRealParam(scip, "limits/memory", &memorylimit) ); /* substract the memory already used by the main SCIP and the estimated memory usage of external software */ if( !SCIPisInfinity(scip, memorylimit) ) { memorylimit -= SCIPgetMemUsed(scip)/1048576.0; memorylimit -= SCIPgetMemExternEstim(scip)/1048576.0; } /* abort if no time is left or not enough memory to create a copy of SCIP, including external memory usage */ if( timelimit <= 0.0 || memorylimit <= 2.0*SCIPgetMemExternEstim(scip)/1048576.0 ) return SCIP_OKAY; *result = SCIP_DIDNOTFIND; /* get variable data */ SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL) ); /* initialize the subproblem */ SCIP_CALL( SCIPcreate(&subscip) ); /* create the variable mapping hash map */ SCIP_CALL( SCIPhashmapCreate(&varmapfw, SCIPblkmem(subscip), SCIPcalcHashtableSize(5 * nvars)) ); SCIP_CALL( SCIPallocBufferArray(scip, &subvars, nvars) ); /* different methods to create sub-problem: either copy LP relaxation or the CIP with all constraints */ valid = FALSE; /* copy complete SCIP instance */ SCIP_CALL( SCIPcopy(scip, subscip, varmapfw, NULL, "zeroobj", TRUE, FALSE, TRUE, &valid) ); SCIPdebugMessage("Copying the SCIP instance was %s complete.\n", valid ? "" : "not "); /* create event handler for LP events */ eventhdlr = NULL; SCIP_CALL( SCIPincludeEventhdlrBasic(subscip, &eventhdlr, EVENTHDLR_NAME, EVENTHDLR_DESC, eventExecZeroobj, NULL) ); if( eventhdlr == NULL ) { SCIPerrorMessage("event handler for "HEUR_NAME" heuristic not found.\n"); return SCIP_PLUGINNOTFOUND; } /* determine large value to set variables to */ large = SCIPinfinity(scip); if( !SCIPisInfinity(scip, 0.1 / SCIPfeastol(scip)) ) large = 0.1 / SCIPfeastol(scip); /* get variable image and change to 0.0 in sub-SCIP */ for( i = 0; i < nvars; i++ ) { SCIP_Real adjustedbound; SCIP_Real lb; SCIP_Real ub; SCIP_Real inf; subvars[i] = (SCIP_VAR*) SCIPhashmapGetImage(varmapfw, vars[i]); SCIP_CALL( SCIPchgVarObj(subscip, subvars[i], 0.0) ); lb = SCIPvarGetLbGlobal(subvars[i]); ub = SCIPvarGetUbGlobal(subvars[i]); inf = SCIPinfinity(subscip); /* adjust infinite bounds in order to avoid that variables with non-zero objective * get fixed to infinite value in zeroobj subproblem */ if( SCIPisInfinity(subscip, ub ) ) { adjustedbound = MAX(large, lb+large); adjustedbound = MIN(adjustedbound, inf); SCIP_CALL( SCIPchgVarUbGlobal(subscip, subvars[i], adjustedbound) ); } if( SCIPisInfinity(subscip, -lb ) ) { adjustedbound = MIN(-large, ub-large); adjustedbound = MAX(adjustedbound, -inf); SCIP_CALL( SCIPchgVarLbGlobal(subscip, subvars[i], adjustedbound) ); } } /* free hash map */ SCIPhashmapFree(&varmapfw); /* do not abort subproblem on CTRL-C */ SCIP_CALL( SCIPsetBoolParam(subscip, "misc/catchctrlc", FALSE) ); /* disable output to console */ SCIP_CALL( SCIPsetIntParam(subscip, "display/verblevel", 0) ); /* set limits for the subproblem */ SCIP_CALL( SCIPsetLongintParam(subscip, "limits/nodes", nnodes) ); SCIP_CALL( SCIPsetRealParam(subscip, "limits/time", timelimit) ); SCIP_CALL( SCIPsetRealParam(subscip, "limits/memory", memorylimit) ); SCIP_CALL( SCIPsetIntParam(subscip, "limits/solutions", 1) ); /* forbid recursive call of heuristics and separators solving sub-SCIPs */ SCIP_CALL( SCIPsetSubscipsOff(subscip, TRUE) ); /* disable expensive techniques that merely work on the dual bound */ /* disable cutting plane separation */ SCIP_CALL( SCIPsetSeparating(subscip, SCIP_PARAMSETTING_OFF, TRUE) ); /* disable expensive presolving */ SCIP_CALL( SCIPsetPresolving(subscip, SCIP_PARAMSETTING_FAST, TRUE) ); if( !SCIPisParamFixed(subscip, "presolving/maxrounds") ) { SCIP_CALL( SCIPsetIntParam(subscip, "presolving/maxrounds", 50) ); } /* use best dfs node selection */ if( SCIPfindNodesel(subscip, "dfs") != NULL && !SCIPisParamFixed(subscip, "nodeselection/dfs/stdpriority") ) { SCIP_CALL( SCIPsetIntParam(subscip, "nodeselection/dfs/stdpriority", INT_MAX/4) ); } /* use inference branching */ if( SCIPfindBranchrule(subscip, "inference") != NULL && !SCIPisParamFixed(subscip, "branching/inference/priority") ) { SCIP_CALL( SCIPsetIntParam(subscip, "branching/leastinf/priority", INT_MAX/4) ); } /* employ a limit on the number of enforcement rounds in the quadratic constraint handler; this fixes the issue that * sometimes the quadratic constraint handler needs hundreds or thousands of enforcement rounds to determine the * feasibility status of a single node without fractional branching candidates by separation (namely for uflquad * instances); however, the solution status of the sub-SCIP might get corrupted by this; hence no deductions shall be * made for the original SCIP */ if( SCIPfindConshdlr(subscip, "quadratic") != NULL && !SCIPisParamFixed(subscip, "constraints/quadratic/enfolplimit") ) { SCIP_CALL( SCIPsetIntParam(subscip, "constraints/quadratic/enfolplimit", 10) ); } /* disable feaspump and fracdiving */ if( !SCIPisParamFixed(subscip, "heuristics/feaspump/freq") ) { SCIP_CALL( SCIPsetIntParam(subscip, "heuristics/feaspump/freq", -1) ); } if( !SCIPisParamFixed(subscip, "heuristics/fracdiving/freq") ) { SCIP_CALL( SCIPsetIntParam(subscip, "heuristics/fracdiving/freq", -1) ); } /* restrict LP iterations */ SCIP_CALL( SCIPsetLongintParam(subscip, "lp/iterlim", 2*heurdata->maxlpiters / MAX(1,nnodes)) ); SCIP_CALL( SCIPsetLongintParam(subscip, "lp/rootiterlim", heurdata->maxlpiters) ); #ifdef SCIP_DEBUG /* for debugging zeroobj, enable MIP output */ SCIP_CALL( SCIPsetIntParam(subscip, "display/verblevel", 5) ); SCIP_CALL( SCIPsetIntParam(subscip, "display/freq", 100000000) ); #endif /* if there is already a solution, add an objective cutoff */ if( SCIPgetNSols(scip) > 0 ) { SCIP_Real upperbound; SCIP_CONS* origobjcons; #ifndef NDEBUG int nobjvars; nobjvars = 0; #endif cutoff = SCIPinfinity(scip); assert( !SCIPisInfinity(scip,SCIPgetUpperbound(scip)) ); upperbound = SCIPgetUpperbound(scip) - SCIPsumepsilon(scip); if( !SCIPisInfinity(scip,-1.0*SCIPgetLowerbound(scip)) ) { cutoff = (1-minimprove)*SCIPgetUpperbound(scip) + minimprove*SCIPgetLowerbound(scip); } else { if( SCIPgetUpperbound(scip) >= 0 ) cutoff = ( 1 - minimprove ) * SCIPgetUpperbound ( scip ); else cutoff = ( 1 + minimprove ) * SCIPgetUpperbound ( scip ); } cutoff = MIN(upperbound, cutoff); SCIP_CALL( SCIPcreateConsLinear(subscip, &origobjcons, "objbound_of_origscip", 0, NULL, NULL, -SCIPinfinity(subscip), cutoff, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) ); for( i = 0; i < nvars; ++i) { if( !SCIPisFeasZero(subscip, SCIPvarGetObj(vars[i])) ) { SCIP_CALL( SCIPaddCoefLinear(subscip, origobjcons, subvars[i], SCIPvarGetObj(vars[i])) ); #ifndef NDEBUG nobjvars++; #endif } } SCIP_CALL( SCIPaddCons(subscip, origobjcons) ); SCIP_CALL( SCIPreleaseCons(subscip, &origobjcons) ); assert(nobjvars == SCIPgetNObjVars(scip)); } /* catch LP events of sub-SCIP */ SCIP_CALL( SCIPtransformProb(subscip) ); SCIP_CALL( SCIPcatchEvent(subscip, SCIP_EVENTTYPE_NODESOLVED, eventhdlr, (SCIP_EVENTDATA*) heurdata, NULL) ); SCIPdebugMessage("solving subproblem: nnodes=%"SCIP_LONGINT_FORMAT"\n", nnodes); retcode = SCIPsolve(subscip); /* drop LP events of sub-SCIP */ SCIP_CALL( SCIPdropEvent(subscip, SCIP_EVENTTYPE_NODESOLVED, eventhdlr, (SCIP_EVENTDATA*) heurdata, -1) ); /* 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(scip, "Error while solving subproblem in zeroobj heuristic; sub-SCIP terminated with code <%d>\n",retcode); } /* check, whether a solution was found; * due to numerics, it might happen that not all solutions are feasible -> try all solutions until one was accepted */ nsubsols = SCIPgetNSols(subscip); subsols = SCIPgetSols(subscip); success = FALSE; for( i = 0; i < nsubsols && (!success || heurdata->addallsols); ++i ) { SCIP_CALL( createNewSol(scip, subscip, subvars, heur, subsols[i], &success) ); if( success ) *result = SCIP_FOUNDSOL; } #ifdef SCIP_DEBUG SCIP_CALL( SCIPprintStatistics(subscip, NULL) ); #endif /* free subproblem */ SCIPfreeBufferArray(scip, &subvars); SCIP_CALL( SCIPfree(&subscip) ); return SCIP_OKAY; }
/** execution method of primal heuristic */ static SCIP_DECL_HEUREXEC(heurExecMutation) { /*lint --e{715}*/ SCIP_Longint maxnnodes; SCIP_Longint nsubnodes; /* node limit for the subproblem */ SCIP_HEURDATA* heurdata; /* heuristic's data */ SCIP* subscip; /* the subproblem created by mutation */ 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_Real cutoff; /* objective cutoff for the subproblem */ SCIP_Real maxnnodesr; SCIP_Real memorylimit; SCIP_Real timelimit; /* timelimit for the subproblem */ SCIP_Real upperbound; int nvars; /* number of original problem's variables */ int i; SCIP_Bool success; SCIP_RETCODE retcode; assert( heur != NULL ); assert( scip != NULL ); assert( result != NULL ); /* get heuristic's data */ heurdata = SCIPheurGetData(heur); assert( heurdata != NULL ); *result = SCIP_DELAYED; /* only call heuristic, if feasible solution is available */ if( SCIPgetNSols(scip) <= 0 ) return SCIP_OKAY; /* only call heuristic, if the best solution comes from transformed problem */ assert( SCIPgetBestSol(scip) != NULL ); if( SCIPsolIsOriginal(SCIPgetBestSol(scip)) ) return SCIP_OKAY; /* only call heuristic, if enough nodes were processed since last incumbent */ if( SCIPgetNNodes(scip) - SCIPgetSolNodenum(scip,SCIPgetBestSol(scip)) < heurdata->nwaitingnodes) return SCIP_OKAY; *result = SCIP_DIDNOTRUN; /* only call heuristic, if discrete variables are present */ if( SCIPgetNBinVars(scip) == 0 && SCIPgetNIntVars(scip) == 0 ) return SCIP_OKAY; /* calculate the maximal number of branching nodes until heuristic is aborted */ maxnnodesr = heurdata->nodesquot * SCIPgetNNodes(scip); /* reward mutation if it succeeded often, count the setup costs for the sub-MIP as 100 nodes */ maxnnodesr *= 1.0 + 2.0 * (SCIPheurGetNBestSolsFound(heur)+1.0)/(SCIPheurGetNCalls(heur) + 1.0); maxnnodes = (SCIP_Longint) maxnnodesr - 100 * SCIPheurGetNCalls(heur); maxnnodes += heurdata->nodesofs; /* determine the node limit for the current process */ nsubnodes = maxnnodes - heurdata->usednodes; nsubnodes = MIN(nsubnodes, heurdata->maxnodes); /* check whether we have enough nodes left to call subproblem solving */ if( nsubnodes < heurdata->minnodes ) 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) ); /* create the variable mapping hash map */ SCIP_CALL( SCIPhashmapCreate(&varmapfw, SCIPblkmem(subscip), SCIPcalcHashtableSize(5 * nvars)) ); if( heurdata->uselprows ) { char probname[SCIP_MAXSTRLEN]; /* copy all plugins */ SCIP_CALL( SCIPincludeDefaultPlugins(subscip) ); /* get name of the original problem and add the string "_mutationsub" */ (void) SCIPsnprintf(probname, SCIP_MAXSTRLEN, "%s_mutationsub", SCIPgetProbName(scip)); /* create the subproblem */ SCIP_CALL( SCIPcreateProb(subscip, probname, NULL, NULL, NULL, NULL, NULL, NULL, NULL) ); /* copy all variables */ SCIP_CALL( SCIPcopyVars(scip, subscip, varmapfw, NULL, TRUE) ); } else { SCIP_Bool valid; valid = FALSE; SCIP_CALL( SCIPcopy(scip, subscip, varmapfw, NULL, "rens", TRUE, FALSE, TRUE, &valid) ); if( heurdata->copycuts ) { /* copies all active cuts from cutpool of sourcescip to linear constraints in targetscip */ SCIP_CALL( SCIPcopyCuts(scip, subscip, varmapfw, NULL, TRUE, NULL) ); } SCIPdebugMessage("Copying the SCIP instance was %s complete.\n", valid ? "" : "not "); } for( i = 0; i < nvars; i++ ) subvars[i] = (SCIP_VAR*) SCIPhashmapGetImage(varmapfw, vars[i]); /* free hash map */ SCIPhashmapFree(&varmapfw); /* create a new problem, which fixes variables with same value in bestsol and LP relaxation */ SCIP_CALL( createSubproblem(scip, subscip, subvars, heurdata->minfixingrate, &heurdata->randseed, heurdata->uselprows) ); /* do not abort subproblem on CTRL-C */ SCIP_CALL( SCIPsetBoolParam(subscip, "misc/catchctrlc", FALSE) ); /* disable output to console */ SCIP_CALL( SCIPsetIntParam(subscip, "display/verblevel", 0) ); /* 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) ); /* substract the memory already used by the main SCIP and the estimated memory usage of external software */ if( !SCIPisInfinity(scip, memorylimit) ) { memorylimit -= SCIPgetMemUsed(scip)/1048576.0; memorylimit -= SCIPgetMemExternEstim(scip)/1048576.0; } /* abort if no time is left or not enough memory to create a copy of SCIP, including external memory usage */ if( timelimit <= 0.0 || memorylimit <= 2.0*SCIPgetMemExternEstim(scip)/1048576.0 ) goto TERMINATE; /* set limits for the subproblem */ SCIP_CALL( SCIPsetLongintParam(subscip, "limits/nodes", nsubnodes) ); SCIP_CALL( SCIPsetRealParam(subscip, "limits/time", timelimit) ); SCIP_CALL( SCIPsetRealParam(subscip, "limits/memory", memorylimit) ); /* 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) ); /* use best estimate node selection */ if( SCIPfindNodesel(subscip, "estimate") != NULL && !SCIPisParamFixed(subscip, "nodeselection/estimate/stdpriority") ) { SCIP_CALL( SCIPsetIntParam(subscip, "nodeselection/estimate/stdpriority", INT_MAX/4) ); } /* use inference branching */ if( SCIPfindBranchrule(subscip, "inference") != NULL && !SCIPisParamFixed(subscip, "branching/inference/priority") ) { SCIP_CALL( SCIPsetIntParam(subscip, "branching/inference/priority", INT_MAX/4) ); } /* disable conflict analysis */ if( !SCIPisParamFixed(subscip, "conflict/useprop") ) { SCIP_CALL( SCIPsetBoolParam(subscip, "conflict/useprop", FALSE) ); } if( !SCIPisParamFixed(subscip, "conflict/useinflp") ) { SCIP_CALL( SCIPsetBoolParam(subscip, "conflict/useinflp", FALSE) ); } if( !SCIPisParamFixed(subscip, "conflict/useboundlp") ) { SCIP_CALL( SCIPsetBoolParam(subscip, "conflict/useboundlp", FALSE) ); } if( !SCIPisParamFixed(subscip, "conflict/usesb") ) { SCIP_CALL( SCIPsetBoolParam(subscip, "conflict/usesb", FALSE) ); } if( !SCIPisParamFixed(subscip, "conflict/usepseudo") ) { SCIP_CALL( SCIPsetBoolParam(subscip, "conflict/usepseudo", FALSE) ); } /* employ a limit on the number of enforcement rounds in the quadratic constraint handlers; this fixes the issue that * sometimes the quadratic constraint handler needs hundreds or thousands of enforcement rounds to determine the * feasibility status of a single node without fractional branching candidates by separation (namely for uflquad * instances); however, the solution status of the sub-SCIP might get corrupted by this; hence no decutions shall be * made for the original SCIP */ if( SCIPfindConshdlr(subscip, "quadratic") != NULL && !SCIPisParamFixed(subscip, "constraints/quadratic/enfolplimit") ) { SCIP_CALL( SCIPsetIntParam(subscip, "constraints/quadratic/enfolplimit", 10) ); } /* add an objective cutoff */ cutoff = SCIPinfinity(scip); assert( !SCIPisInfinity(scip, SCIPgetUpperbound(scip)) ); upperbound = SCIPgetUpperbound(scip) - SCIPsumepsilon(scip); if( !SCIPisInfinity(scip, -1.0 * SCIPgetLowerbound(scip)) ) { cutoff = (1-heurdata->minimprove) * SCIPgetUpperbound(scip) + heurdata->minimprove * SCIPgetLowerbound(scip); } else { if( SCIPgetUpperbound ( scip ) >= 0 ) cutoff = ( 1 - heurdata->minimprove ) * SCIPgetUpperbound ( scip ); else cutoff = ( 1 + heurdata->minimprove ) * SCIPgetUpperbound ( scip ); } cutoff = MIN(upperbound, cutoff ); SCIP_CALL( SCIPsetObjlimit(subscip, cutoff) ); /* solve the subproblem */ SCIPdebugMessage("Solve Mutation subMIP\n"); 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(scip, "Error while solving subproblem in Mutation heuristic; sub-SCIP terminated with code <%d>\n",retcode); } heurdata->usednodes += SCIPgetNNodes(subscip); /* check, whether a solution was found */ if( SCIPgetNSols(subscip) > 0 ) { 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 one was accepted */ nsubsols = SCIPgetNSols(subscip); subsols = SCIPgetSols(subscip); success = FALSE; for( i = 0; i < nsubsols && !success; ++i ) { SCIP_CALL( createNewSol(scip, subscip, subvars, heur, subsols[i], &success) ); } if( success ) *result = SCIP_FOUNDSOL; } TERMINATE: /* free subproblem */ SCIPfreeBufferArray(scip, &subvars); SCIP_CALL( SCIPfree(&subscip) ); return SCIP_OKAY; }
/** main procedure of the RENS heuristic, creates and solves a subMIP */ SCIP_RETCODE SCIPapplyGcgrens( SCIP* scip, /**< original SCIP data structure */ SCIP_HEUR* heur, /**< heuristic data structure */ SCIP_RESULT* result, /**< result data structure */ SCIP_Real minfixingrate, /**< minimum percentage of integer variables that have to be fixed */ SCIP_Real minimprove, /**< factor by which RENS should at least improve the incumbent */ SCIP_Longint maxnodes, /**< maximum number of nodes for the subproblem */ SCIP_Longint nstallnodes, /**< number of stalling nodes for the subproblem */ SCIP_Bool binarybounds, /**< should general integers get binary bounds [floor(.),ceil(.)]? */ SCIP_Bool uselprows /**< should subproblem be created out of the rows in the LP rows? */ ) { SCIP* subscip; /* the subproblem created by RENS */ SCIP_HASHMAP* varmapfw; /* mapping of SCIP variables to sub-SCIP variables */ SCIP_VAR** vars; /* original problem's variables */ SCIP_VAR** subvars; /* subproblem's variables */ SCIP_Real cutoff; /* objective cutoff for the subproblem */ SCIP_Real timelimit; SCIP_Real memorylimit; int nvars; int i; SCIP_Bool success; SCIP_RETCODE retcode; assert(scip != NULL); assert(heur != NULL); assert(result != NULL); assert(maxnodes >= 0); assert(nstallnodes >= 0); assert(0.0 <= minfixingrate && minfixingrate <= 1.0); assert(0.0 <= minimprove && minimprove <= 1.0); SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL) ); /* initialize the subproblem */ SCIP_CALL( SCIPcreate(&subscip) ); /* create the variable mapping hash map */ SCIP_CALL( SCIPhashmapCreate(&varmapfw, SCIPblkmem(subscip), SCIPcalcHashtableSize(5 * nvars)) ); SCIP_CALL( SCIPallocBufferArray(scip, &subvars, nvars) ); if( uselprows ) { char probname[SCIP_MAXSTRLEN]; /* copy all plugins */ SCIP_CALL( SCIPincludeDefaultPlugins(subscip) ); /* get name of the original problem and add the string "_gcgrenssub" */ (void) SCIPsnprintf(probname, SCIP_MAXSTRLEN, "%s_gcgrenssub", SCIPgetProbName(scip)); /* create the subproblem */ SCIP_CALL( SCIPcreateProb(subscip, probname, NULL, NULL, NULL, NULL, NULL, NULL, NULL) ); /* copy all variables */ SCIP_CALL( SCIPcopyVars(scip, subscip, varmapfw, NULL, TRUE) ); } else { SCIP_Bool valid; SCIP_HEURDATA* heurdata; valid = FALSE; SCIP_CALL( SCIPcopy(scip, subscip, varmapfw, NULL, "gcgrens", TRUE, FALSE, TRUE, &valid) ); /** @todo check for thread safeness */ /* get heuristic's data */ heurdata = SCIPheurGetData(heur); assert( heurdata != NULL ); if( heurdata->copycuts ) { /** copies all active cuts from cutpool of sourcescip to linear constraints in targetscip */ SCIP_CALL( SCIPcopyCuts(scip, subscip, varmapfw, NULL, TRUE, NULL) ); } SCIPdebugMessage("Copying the SCIP instance was %s complete.\n", valid ? "" : "not "); } for( i = 0; i < nvars; i++ ) subvars[i] = (SCIP_VAR*) SCIPhashmapGetImage(varmapfw, vars[i]); /* free hash map */ SCIPhashmapFree(&varmapfw); /* create a new problem, which fixes variables with same value in bestsol and LP relaxation */ SCIP_CALL( createSubproblem(scip, subscip, subvars, minfixingrate, binarybounds, uselprows, &success) ); SCIPdebugMessage("RENS subproblem: %d vars, %d cons, success=%u\n", SCIPgetNVars(subscip), SCIPgetNConss(subscip), success); /* do not abort subproblem on CTRL-C */ SCIP_CALL( SCIPsetBoolParam(subscip, "misc/catchctrlc", FALSE) ); /* disable output to console */ SCIP_CALL( SCIPsetIntParam(subscip, "display/verblevel", 0) ); /* check whether there is enough time and memory left */ timelimit = 0.0; memorylimit = 0.0; 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; /* set limits for the subproblem */ SCIP_CALL( SCIPsetLongintParam(subscip, "limits/stallnodes", nstallnodes) ); SCIP_CALL( SCIPsetLongintParam(subscip, "limits/nodes", maxnodes) ); SCIP_CALL( SCIPsetRealParam(subscip, "limits/time", timelimit) ); SCIP_CALL( SCIPsetRealParam(subscip, "limits/memory", memorylimit) ); /* forbid recursive call of heuristics and separators solving sub-SCIPs */ 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) ); /* use best estimate node selection */ if( SCIPfindNodesel(scip, "estimate") != NULL ) { SCIP_CALL( SCIPsetIntParam(subscip, "nodeselection/estimate/stdpriority", INT_MAX/4) ); } /* use inference branching */ if( SCIPfindBranchrule(scip, "inference") != NULL ) { SCIP_CALL( SCIPsetIntParam(subscip, "branching/inference/priority", INT_MAX/4) ); } /* disable conflict analysis */ SCIP_CALL( SCIPsetBoolParam(subscip, "conflict/useprop", FALSE) ); SCIP_CALL( SCIPsetBoolParam(subscip, "conflict/useinflp", FALSE) ); SCIP_CALL( SCIPsetBoolParam(subscip, "conflict/useboundlp", FALSE) ); SCIP_CALL( SCIPsetBoolParam(subscip, "conflict/usesb", FALSE) ); SCIP_CALL( SCIPsetBoolParam(subscip, "conflict/usepseudo", FALSE) ); #ifdef SCIP_DEBUG /* for debugging RENS, enable MIP output */ SCIP_CALL( SCIPsetIntParam(subscip, "display/verblevel", 5) ); SCIP_CALL( SCIPsetIntParam(subscip, "display/freq", 100000000) ); #endif /* if the subproblem could not be created, free memory and return */ if( !success ) { *result = SCIP_DIDNOTRUN; SCIPfreeBufferArray(scip, &subvars); SCIP_CALL( SCIPfree(&subscip) ); return SCIP_OKAY; } /* if there is already a solution, add an objective cutoff */ if( SCIPgetNSols(scip) > 0 ) { SCIP_Real upperbound; assert( !SCIPisInfinity(scip,SCIPgetUpperbound(scip)) ); upperbound = SCIPgetUpperbound(scip) - SCIPsumepsilon(scip); if( !SCIPisInfinity(scip,-1.0*SCIPgetLowerbound(scip)) ) { cutoff = (1-minimprove)*SCIPgetUpperbound(scip) + minimprove*SCIPgetLowerbound(scip); } else { if( SCIPgetUpperbound ( scip ) >= 0 ) cutoff = ( 1 - minimprove ) * SCIPgetUpperbound ( scip ); else cutoff = ( 1 + minimprove ) * SCIPgetUpperbound ( scip ); } cutoff = MIN(upperbound, cutoff); SCIP_CALL( SCIPsetObjlimit(subscip, cutoff) ); } /* presolve the subproblem */ retcode = SCIPpresolve(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(scip, "Error while presolving subproblem in GCG RENS heuristic; sub-SCIP terminated with code <%d>\n",retcode); } SCIPdebugMessage("GCG RENS presolved subproblem: %d vars, %d cons, success=%u\n", SCIPgetNVars(subscip), SCIPgetNConss(subscip), success); /* after presolving, we should have at least reached a certain fixing rate over ALL variables (including continuous) * to ensure that not only the MIP but also the LP relaxation is easy enough */ if( ( nvars - SCIPgetNVars(subscip) ) / (SCIP_Real)nvars >= minfixingrate / 2.0 ) { SCIP_SOL** subsols; int nsubsols; /* solve the subproblem */ SCIPdebugMessage("solving subproblem: nstallnodes=%"SCIP_LONGINT_FORMAT", maxnodes=%"SCIP_LONGINT_FORMAT"\n", nstallnodes, maxnodes); 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(scip, "Error while solving subproblem in GCG RENS heuristic; sub-SCIP terminated with code <%d>\n",retcode); } /* check, whether a solution was found; * due to numerics, it might happen that not all solutions are feasible -> try all solutions until one was accepted */ nsubsols = SCIPgetNSols(subscip); subsols = SCIPgetSols(subscip); success = FALSE; for( i = 0; i < nsubsols && !success; ++i ) { SCIP_CALL( createNewSol(scip, subscip, subvars, heur, subsols[i], &success) ); } if( success ) *result = SCIP_FOUNDSOL; } TERMINATE: /* free subproblem */ SCIPfreeBufferArray(scip, &subvars); SCIP_CALL( SCIPfree(&subscip) ); return SCIP_OKAY; }
/** execution method of primal heuristic */ static SCIP_DECL_HEUREXEC(heurExecLocalbranching) { /*lint --e{715}*/ SCIP_Longint maxnnodes; /* maximum number of subnodes */ SCIP_Longint nsubnodes; /* nodelimit for subscip */ SCIP_HEURDATA* heurdata; SCIP* subscip; /* the subproblem created by localbranching */ SCIP_VAR** subvars; /* subproblem's variables */ SCIP_SOL* bestsol; /* best solution so far */ SCIP_EVENTHDLR* eventhdlr; /* event handler for LP events */ SCIP_Real timelimit; /* timelimit for subscip (equals remaining time of scip) */ SCIP_Real cutoff; /* objective cutoff for the subproblem */ SCIP_Real upperbound; SCIP_Real memorylimit; SCIP_HASHMAP* varmapfw; /* mapping of SCIP variables to sub-SCIP variables */ SCIP_VAR** vars; int nvars; int i; SCIP_Bool success; SCIP_RETCODE retcode; assert(heur != NULL); assert(scip != NULL); assert(result != NULL); *result = SCIP_DIDNOTRUN; /* get heuristic's data */ heurdata = SCIPheurGetData(heur); assert( heurdata != NULL ); /* there should be enough binary variables that a local branching constraint makes sense */ if( SCIPgetNBinVars(scip) < 2*heurdata->neighborhoodsize ) return SCIP_OKAY; *result = SCIP_DELAYED; /* only call heuristic, if an IP solution is at hand */ if( SCIPgetNSols(scip) <= 0 ) return SCIP_OKAY; bestsol = SCIPgetBestSol(scip); assert(bestsol != NULL); /* only call heuristic, if the best solution comes from transformed problem */ if( SCIPsolIsOriginal(bestsol) ) return SCIP_OKAY; /* only call heuristic, if enough nodes were processed since last incumbent */ if( SCIPgetNNodes(scip) - SCIPgetSolNodenum(scip, bestsol) < heurdata->nwaitingnodes) return SCIP_OKAY; /* only call heuristic, if the best solution does not come from trivial heuristic */ if( SCIPsolGetHeur(bestsol) != NULL && strcmp(SCIPheurGetName(SCIPsolGetHeur(bestsol)), "trivial") == 0 ) return SCIP_OKAY; /* reset neighborhood and minnodes, if new solution was found */ if( heurdata->lastsol != bestsol ) { heurdata->curneighborhoodsize = heurdata->neighborhoodsize; heurdata->curminnodes = heurdata->minnodes; heurdata->emptyneighborhoodsize = 0; heurdata->callstatus = EXECUTE; heurdata->lastsol = bestsol; } /* if no new solution was found and local branching also seems to fail, just keep on waiting */ if( heurdata->callstatus == WAITFORNEWSOL ) return SCIP_OKAY; *result = SCIP_DIDNOTRUN; /* calculate the maximal number of branching nodes until heuristic is aborted */ maxnnodes = (SCIP_Longint)(heurdata->nodesquot * SCIPgetNNodes(scip)); /* reward local branching if it succeeded often */ maxnnodes = (SCIP_Longint)(maxnnodes * (1.0 + 2.0*(SCIPheurGetNBestSolsFound(heur)+1.0)/(SCIPheurGetNCalls(heur)+1.0))); maxnnodes -= 100 * SCIPheurGetNCalls(heur); /* count the setup costs for the sub-MIP as 100 nodes */ maxnnodes += heurdata->nodesofs; /* determine the node limit for the current process */ nsubnodes = maxnnodes - heurdata->usednodes; nsubnodes = MIN(nsubnodes, heurdata->maxnodes); /* check whether we have enough nodes left to call sub problem solving */ if( nsubnodes < heurdata->curminnodes ) return SCIP_OKAY; if( SCIPisStopped(scip) ) return SCIP_OKAY; *result = SCIP_DIDNOTFIND; SCIPdebugMessage("running localbranching heuristic ...\n"); /* get the data of the variables and the best solution */ SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL) ); /* initializing the subproblem */ SCIP_CALL( SCIPallocBufferArray(scip, &subvars, nvars) ); SCIP_CALL( SCIPcreate(&subscip) ); /* create the variable mapping hash map */ SCIP_CALL( SCIPhashmapCreate(&varmapfw, SCIPblkmem(subscip), SCIPcalcHashtableSize(5 * nvars)) ); success = FALSE; eventhdlr = NULL; if( heurdata->uselprows ) { char probname[SCIP_MAXSTRLEN]; /* copy all plugins */ SCIP_CALL( SCIPincludeDefaultPlugins(subscip) ); /* get name of the original problem and add the string "_localbranchsub" */ (void) SCIPsnprintf(probname, SCIP_MAXSTRLEN, "%s_localbranchsub", SCIPgetProbName(scip)); /* create the subproblem */ SCIP_CALL( SCIPcreateProb(subscip, probname, NULL, NULL, NULL, NULL, NULL, NULL, NULL) ); /* copy all variables */ SCIP_CALL( SCIPcopyVars(scip, subscip, varmapfw, NULL, TRUE) ); } else { SCIP_CALL( SCIPcopy(scip, subscip, varmapfw, NULL, "localbranchsub", TRUE, FALSE, TRUE, &success) ); if( heurdata->copycuts ) { /* copies all active cuts from cutpool of sourcescip to linear constraints in targetscip */ SCIP_CALL( SCIPcopyCuts(scip, subscip, varmapfw, NULL, TRUE, NULL) ); } /* create event handler for LP events */ SCIP_CALL( SCIPincludeEventhdlrBasic(subscip, &eventhdlr, EVENTHDLR_NAME, EVENTHDLR_DESC, eventExecLocalbranching, NULL) ); if( eventhdlr == NULL ) { SCIPerrorMessage("event handler for "HEUR_NAME" heuristic not found.\n"); return SCIP_PLUGINNOTFOUND; } } SCIPdebugMessage("Copying the plugins was %ssuccessful.\n", success ? "" : "not "); for (i = 0; i < nvars; ++i) subvars[i] = (SCIP_VAR*) SCIPhashmapGetImage(varmapfw, vars[i]); /* free hash map */ SCIPhashmapFree(&varmapfw); /* if the subproblem could not be created, free memory and return */ if( !success ) { *result = SCIP_DIDNOTRUN; goto TERMINATE; } /* 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 /* 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) ); /* substract the memory already used by the main SCIP and the estimated memory usage of external software */ if( !SCIPisInfinity(scip, memorylimit) ) { memorylimit -= SCIPgetMemUsed(scip)/1048576.0; memorylimit -= SCIPgetMemExternEstim(scip)/1048576.0; } /* abort if no time is left or not enough memory to create a copy of SCIP, including external memory usage */ if( timelimit <= 0.0 || memorylimit <= 2.0*SCIPgetMemExternEstim(scip)/1048576.0 ) goto TERMINATE; /* set limits for the subproblem */ heurdata->nodelimit = nsubnodes; SCIP_CALL( SCIPsetLongintParam(subscip, "limits/nodes", nsubnodes) ); SCIP_CALL( SCIPsetLongintParam(subscip, "limits/stallnodes", MAX(10, nsubnodes/10)) ); SCIP_CALL( SCIPsetIntParam(subscip, "limits/bestsol", 3) ); SCIP_CALL( SCIPsetRealParam(subscip, "limits/time", timelimit) ); SCIP_CALL( SCIPsetRealParam(subscip, "limits/memory", memorylimit) ); /* 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) ); /* use best estimate node selection */ if( SCIPfindNodesel(subscip, "estimate") != NULL && !SCIPisParamFixed(subscip, "nodeselection/estimate/stdpriority") ) { SCIP_CALL( SCIPsetIntParam(subscip, "nodeselection/estimate/stdpriority", INT_MAX/4) ); } /* use inference branching */ if( SCIPfindBranchrule(subscip, "inference") != NULL && !SCIPisParamFixed(subscip, "branching/inference/priority") ) { SCIP_CALL( SCIPsetIntParam(subscip, "branching/inference/priority", INT_MAX/4) ); } /* disable conflict analysis */ if( !SCIPisParamFixed(subscip, "conflict/useprop") ) { SCIP_CALL( SCIPsetBoolParam(subscip, "conflict/useprop", FALSE) ); } if( !SCIPisParamFixed(subscip, "conflict/useinflp") ) { SCIP_CALL( SCIPsetBoolParam(subscip, "conflict/useinflp", FALSE) ); } if( !SCIPisParamFixed(subscip, "conflict/useboundlp") ) { SCIP_CALL( SCIPsetBoolParam(subscip, "conflict/useboundlp", FALSE) ); } if( !SCIPisParamFixed(subscip, "conflict/usesb") ) { SCIP_CALL( SCIPsetBoolParam(subscip, "conflict/usesb", FALSE) ); } if( !SCIPisParamFixed(subscip, "conflict/usepseudo") ) { SCIP_CALL( SCIPsetBoolParam(subscip, "conflict/usepseudo", FALSE) ); } /* employ a limit on the number of enforcement rounds in the quadratic constraint handler; this fixes the issue that * sometimes the quadratic constraint handler needs hundreds or thousands of enforcement rounds to determine the * feasibility status of a single node without fractional branching candidates by separation (namely for uflquad * instances); however, the solution status of the sub-SCIP might get corrupted by this; hence no deductions shall be * made for the original SCIP */ if( SCIPfindConshdlr(subscip, "quadratic") != NULL && !SCIPisParamFixed(subscip, "constraints/quadratic/enfolplimit") ) { SCIP_CALL( SCIPsetIntParam(subscip, "constraints/quadratic/enfolplimit", 500) ); } /* copy the original problem and add the local branching constraint */ if( heurdata->uselprows ) { SCIP_CALL( createSubproblem(scip, subscip, subvars) ); } SCIP_CALL( addLocalBranchingConstraint(scip, subscip, subvars, heurdata) ); /* add an objective cutoff */ cutoff = SCIPinfinity(scip); assert( !SCIPisInfinity(scip,SCIPgetUpperbound(scip)) ); upperbound = SCIPgetUpperbound(scip) - SCIPsumepsilon(scip); if( !SCIPisInfinity(scip,-1.0*SCIPgetLowerbound(scip)) ) { cutoff = (1-heurdata->minimprove)*SCIPgetUpperbound(scip) + heurdata->minimprove*SCIPgetLowerbound(scip); } else { if( SCIPgetUpperbound ( scip ) >= 0 ) cutoff = ( 1 - heurdata->minimprove ) * SCIPgetUpperbound ( scip ); else cutoff = ( 1 + heurdata->minimprove ) * SCIPgetUpperbound ( scip ); } cutoff = MIN(upperbound, cutoff ); SCIP_CALL( SCIPsetObjlimit(subscip, cutoff) ); /* catch LP events of sub-SCIP */ if( !heurdata->uselprows ) { assert(eventhdlr != NULL); SCIP_CALL( SCIPtransformProb(subscip) ); SCIP_CALL( SCIPcatchEvent(subscip, SCIP_EVENTTYPE_LPSOLVED, eventhdlr, (SCIP_EVENTDATA*) heurdata, NULL) ); } /* solve the subproblem */ SCIPdebugMessage("solving local branching subproblem with neighborhoodsize %d and maxnodes %"SCIP_LONGINT_FORMAT"\n", heurdata->curneighborhoodsize, nsubnodes); retcode = SCIPsolve(subscip); /* drop LP events of sub-SCIP */ if( !heurdata->uselprows ) { assert(eventhdlr != NULL); SCIP_CALL( SCIPdropEvent(subscip, SCIP_EVENTTYPE_LPSOLVED, eventhdlr, (SCIP_EVENTDATA*) heurdata, -1) ); } /* 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(scip, "Error while solving subproblem in local branching heuristic; sub-SCIP terminated with code <%d>\n",retcode); } /* print solving statistics of subproblem if we are in SCIP's debug mode */ SCIPdebug( SCIP_CALL( SCIPprintStatistics(subscip, NULL) ) ); heurdata->usednodes += SCIPgetNNodes(subscip); SCIPdebugMessage("local branching used %"SCIP_LONGINT_FORMAT"/%"SCIP_LONGINT_FORMAT" nodes\n", SCIPgetNNodes(subscip), nsubnodes); /* check, whether a solution was found */ if( SCIPgetNSols(subscip) > 0 ) { 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 one was accepted */ nsubsols = SCIPgetNSols(subscip); subsols = SCIPgetSols(subscip); success = FALSE; for( i = 0; i < nsubsols && !success; ++i ) { SCIP_CALL( createNewSol(scip, subscip, subvars, heur, subsols[i], &success) ); } if( success ) { SCIPdebugMessage("-> accepted solution of value %g\n", SCIPgetSolOrigObj(subscip, subsols[i])); *result = SCIP_FOUNDSOL; } } /* check the status of the sub-MIP */ switch( SCIPgetStatus(subscip) ) { case SCIP_STATUS_OPTIMAL: case SCIP_STATUS_BESTSOLLIMIT: heurdata->callstatus = WAITFORNEWSOL; /* new solution will immediately be installed at next call */ SCIPdebugMessage(" -> found new solution\n"); break; case SCIP_STATUS_NODELIMIT: case SCIP_STATUS_STALLNODELIMIT: case SCIP_STATUS_TOTALNODELIMIT: heurdata->callstatus = EXECUTE; heurdata->curneighborhoodsize = (heurdata->emptyneighborhoodsize + heurdata->curneighborhoodsize)/2; heurdata->curminnodes *= 2; SCIPdebugMessage(" -> node limit reached: reduced neighborhood to %d, increased minnodes to %d\n", heurdata->curneighborhoodsize, heurdata->curminnodes); if( heurdata->curneighborhoodsize <= heurdata->emptyneighborhoodsize ) { heurdata->callstatus = WAITFORNEWSOL; SCIPdebugMessage(" -> new neighborhood was already proven to be empty: wait for new solution\n"); } break; case SCIP_STATUS_INFEASIBLE: case SCIP_STATUS_INFORUNBD: heurdata->emptyneighborhoodsize = heurdata->curneighborhoodsize; heurdata->curneighborhoodsize += heurdata->curneighborhoodsize/2; heurdata->curneighborhoodsize = MAX(heurdata->curneighborhoodsize, heurdata->emptyneighborhoodsize + 2); heurdata->callstatus = EXECUTE; SCIPdebugMessage(" -> neighborhood is empty: increased neighborhood to %d\n", heurdata->curneighborhoodsize); break; case SCIP_STATUS_UNKNOWN: case SCIP_STATUS_USERINTERRUPT: case SCIP_STATUS_TIMELIMIT: case SCIP_STATUS_MEMLIMIT: case SCIP_STATUS_GAPLIMIT: case SCIP_STATUS_SOLLIMIT: case SCIP_STATUS_UNBOUNDED: default: heurdata->callstatus = WAITFORNEWSOL; SCIPdebugMessage(" -> unexpected sub-MIP status <%d>: waiting for new solution\n", SCIPgetStatus(subscip)); break; } TERMINATE: /* free subproblem */ SCIPfreeBufferArray(scip, &subvars); SCIP_CALL( SCIPfree(&subscip) ); return SCIP_OKAY; }
/** branching execution method for fractional LP solutions */ static SCIP_DECL_BRANCHEXECLP(branchExeclpStp) { /*lint --e{715}*/ SCIP_PROBDATA* probdata; SCIP_CONS* consin; SCIP_CONS* consout; SCIP_NODE* vertexin; SCIP_NODE* vertexout; SCIP_VAR** edgevars; SCIP_Real estimatein; SCIP_Real estimateout; GRAPH* g; int e; int branchvertex; assert(branchrule != NULL); assert(strcmp(SCIPbranchruleGetName(branchrule), BRANCHRULE_NAME) == 0); assert(scip != NULL); assert(result != NULL); SCIPdebugMessage("Execlp method of Stp branching\n "); estimatein = SCIPgetUpperbound(scip); estimateout = SCIPgetUpperbound(scip); *result = SCIP_DIDNOTRUN; /* get problem data */ probdata = SCIPgetProbData(scip); assert(probdata != NULL); /* get graph */ g = SCIPprobdataGetGraph(probdata); assert(g != NULL); /* get vertex to branch on */ SCIP_CALL( selectBranchingVertex(scip, &branchvertex) ); if( branchvertex == UNKNOWN ) { SCIPdebugMessage("Branching did not run \n"); return SCIP_OKAY; } edgevars = SCIPprobdataGetEdgeVars(scip); /* create constraints */ SCIP_CALL( SCIPcreateConsLinear(scip, &consin, "consin", 0, NULL, NULL, 1.0, 1.0, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE) ); SCIP_CALL( SCIPcreateConsLinear(scip, &consout, "consout", 0, NULL, NULL, 0.0, 0.0, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE) ); for( e = g->inpbeg[branchvertex]; e != EAT_LAST; e = g->ieat[e] ) { SCIP_CALL( SCIPaddCoefLinear(scip, consin, edgevars[e], 1.0) ); SCIP_CALL( SCIPaddCoefLinear(scip, consout, edgevars[e], 1.0) ); SCIP_CALL( SCIPaddCoefLinear(scip, consout, edgevars[flipedge(e)], 1.0) ); } /* create the child nodes */ SCIP_CALL( SCIPcreateChild(scip, &vertexin, 1.0, estimatein) ); SCIP_CALL( SCIPcreateChild(scip, &vertexout, 1.0, estimateout) ); assert(vertexin != NULL); assert(vertexout != NULL); SCIP_CALL( SCIPaddConsNode(scip, vertexin, consin, NULL) ); SCIP_CALL( SCIPaddConsNode(scip, vertexout, consout, NULL) ); /* relase constraints */ SCIP_CALL( SCIPreleaseCons(scip, &consin) ); SCIP_CALL( SCIPreleaseCons(scip, &consout) ); SCIPdebugMessage("Branched on stp vertex %d \n", branchvertex); *result = SCIP_BRANCHED; return SCIP_OKAY; }
/** execution method of primal heuristic */ static SCIP_DECL_HEUREXEC(heurExecCrossover) { /*lint --e{715}*/ SCIP_HEURDATA* heurdata; /* primal heuristic data */ SCIP* subscip; /* the subproblem created by crossover */ SCIP_HASHMAP* varmapfw; /* mapping of SCIP variables to sub-SCIP variables */ SCIP_VAR** vars; /* original problem's variables */ SCIP_VAR** subvars; /* subproblem's variables */ SCIP_SOL** sols; SCIP_Real memorylimit; /* memory limit for the subproblem */ SCIP_Real timelimit; /* time limit for the subproblem */ SCIP_Real cutoff; /* objective cutoff for the subproblem */ SCIP_Real upperbound; SCIP_Bool success; SCIP_Longint nstallnodes; /* node limit for the subproblem */ int* selection; /* pool of solutions crossover uses */ int nvars; /* number of original problem's variables */ int nbinvars; int nintvars; int nusedsols; int i; SCIP_RETCODE retcode; assert(heur != NULL); assert(scip != NULL); assert(result != NULL); /* get heuristic's data */ heurdata = SCIPheurGetData(heur); assert(heurdata != NULL); nusedsols = heurdata->nusedsols; *result = SCIP_DELAYED; /* only call heuristic, if enough solutions are at hand */ if( SCIPgetNSols(scip) < nusedsols ) return SCIP_OKAY; sols = SCIPgetSols(scip); assert(sols != NULL); /* if one good solution was found, heuristic should not be delayed any longer */ if( sols[nusedsols-1] != heurdata->prevlastsol ) { heurdata->nextnodenumber = SCIPgetNNodes(scip); if( sols[0] != heurdata->prevbestsol ) heurdata->nfailures = 0; } /* in nonrandomized mode: only recall heuristic, if at least one new good solution was found in the meantime */ else if( !heurdata->randomization ) return SCIP_OKAY; /* if heuristic should be delayed, wait until certain number of nodes is reached */ if( SCIPgetNNodes(scip) < heurdata->nextnodenumber ) return SCIP_OKAY; /* only call heuristic, if enough nodes were processed since last incumbent */ if( SCIPgetNNodes(scip) - SCIPgetSolNodenum(scip,SCIPgetBestSol(scip)) < heurdata->nwaitingnodes && (SCIPgetDepth(scip) > 0 || !heurdata->dontwaitatroot) ) return SCIP_OKAY; *result = SCIP_DIDNOTRUN; /* calculate the maximal number of branching nodes until heuristic is aborted */ nstallnodes = (SCIP_Longint)(heurdata->nodesquot * SCIPgetNNodes(scip)); /* reward Crossover if it succeeded often */ nstallnodes = (SCIP_Longint) (nstallnodes * (1.0 + 2.0*(SCIPheurGetNBestSolsFound(heur)+1.0)/(SCIPheurGetNCalls(heur)+1.0))); /* count the setup costs for the sub-MIP as 100 nodes */ nstallnodes -= 100 * SCIPheurGetNCalls(heur); nstallnodes += heurdata->nodesofs; /* determine the node limit for the current process */ nstallnodes -= heurdata->usednodes; nstallnodes = MIN(nstallnodes, heurdata->maxnodes); /* check whether we have enough nodes left to call subproblem solving */ if( nstallnodes < heurdata->minnodes ) return SCIP_OKAY; if( SCIPisStopped(scip) ) return SCIP_OKAY; *result = SCIP_DIDNOTFIND; SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, &nbinvars, &nintvars, NULL, NULL) ); assert(nvars > 0); /* check whether discrete variables are available */ if( nbinvars == 0 && nintvars == 0 ) return SCIP_OKAY; /* initializing the subproblem */ SCIP_CALL( SCIPcreate(&subscip) ); /* create the variable mapping hash map */ SCIP_CALL( SCIPhashmapCreate(&varmapfw, SCIPblkmem(subscip), SCIPcalcHashtableSize(5 * nvars)) ); success = FALSE; if( heurdata->uselprows ) { char probname[SCIP_MAXSTRLEN]; /* copy all plugins */ SCIP_CALL( SCIPincludeDefaultPlugins(subscip) ); /* get name of the original problem and add the string "_crossoversub" */ (void) SCIPsnprintf(probname, SCIP_MAXSTRLEN, "%s_crossoversub", SCIPgetProbName(scip)); /* create the subproblem */ SCIP_CALL( SCIPcreateProb(subscip, probname, NULL, NULL, NULL, NULL, NULL, NULL, NULL) ); /* copy all variables */ SCIP_CALL( SCIPcopyVars(scip, subscip, varmapfw, NULL, TRUE) ); } else { SCIP_CALL( SCIPcopy(scip, subscip, varmapfw, NULL, "crossover", TRUE, FALSE, TRUE, &success) ); if( heurdata->copycuts ) { /** copies all active cuts from cutpool of sourcescip to linear constraints in targetscip */ SCIP_CALL( SCIPcopyCuts(scip, subscip, varmapfw, NULL, TRUE, NULL) ); } } SCIP_CALL( SCIPallocBufferArray(scip, &subvars, nvars) ); SCIP_CALL( SCIPallocBufferArray(scip, &selection, nusedsols) ); for( i = 0; i < nvars; i++ ) subvars[i] = (SCIP_VAR*) (size_t) SCIPhashmapGetImage(varmapfw, vars[i]); /* free hash map */ SCIPhashmapFree(&varmapfw); success = FALSE; /* create a new problem, which fixes variables with same value in a certain set of solutions */ SCIP_CALL( setupSubproblem(scip, subscip, subvars, selection, heurdata, &success) ); heurdata->prevbestsol = SCIPgetBestSol(scip); heurdata->prevlastsol = sols[heurdata->nusedsols-1]; /* if creation of sub-SCIP was aborted (e.g. due to number of fixings), free sub-SCIP and abort */ if( !success ) { *result = SCIP_DIDNOTRUN; /* this run will be counted as a failure since no new solution tuple could be generated or the neighborhood of the * solution was not fruitful in the sense that it was too big */ updateFailureStatistic(scip, heurdata); goto TERMINATE; } /* do not abort subproblem on CTRL-C */ SCIP_CALL( SCIPsetBoolParam(subscip, "misc/catchctrlc", FALSE) ); /* disable output to console */ SCIP_CALL( SCIPsetIntParam(subscip, "display/verblevel", 0) ); /* 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) ); /* substract the memory already used by the main SCIP and the estimated memory usage of external software */ if( !SCIPisInfinity(scip, memorylimit) ) { memorylimit -= SCIPgetMemUsed(scip)/1048576.0; memorylimit -= SCIPgetMemExternEstim(scip)/1048576.0; } /* abort if no time is left or not enough memory to create a copy of SCIP, including external memory usage */ if( timelimit <= 0.0 || memorylimit <= 2.0*SCIPgetMemExternEstim(scip)/1048576.0 ) goto TERMINATE; /* set limits for the subproblem */ SCIP_CALL( SCIPsetLongintParam(subscip, "limits/nodes", nstallnodes) ); SCIP_CALL( SCIPsetRealParam(subscip, "limits/time", timelimit) ); SCIP_CALL( SCIPsetRealParam(subscip, "limits/memory", memorylimit) ); /* 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) ); /* use best estimate node selection */ if( SCIPfindNodesel(subscip, "estimate") != NULL && !SCIPisParamFixed(subscip, "nodeselection/estimate/stdpriority") ) { SCIP_CALL( SCIPsetIntParam(subscip, "nodeselection/estimate/stdpriority", INT_MAX/4) ); } /* use inference branching */ if( SCIPfindBranchrule(subscip, "inference") != NULL && !SCIPisParamFixed(subscip, "branching/inference/priority") ) { SCIP_CALL( SCIPsetIntParam(subscip, "branching/inference/priority", INT_MAX/4) ); } /* disable conflict analysis */ if( !SCIPisParamFixed(subscip, "conflict/useprop") ) { SCIP_CALL( SCIPsetBoolParam(subscip, "conflict/useprop", FALSE) ); } if( !SCIPisParamFixed(subscip, "conflict/useinflp") ) { SCIP_CALL( SCIPsetBoolParam(subscip, "conflict/useinflp", FALSE) ); } if( !SCIPisParamFixed(subscip, "conflict/useboundlp") ) { SCIP_CALL( SCIPsetBoolParam(subscip, "conflict/useboundlp", FALSE) ); } if( !SCIPisParamFixed(subscip, "conflict/usesb") ) { SCIP_CALL( SCIPsetBoolParam(subscip, "conflict/usesb", FALSE) ); } if( !SCIPisParamFixed(subscip, "conflict/usepseudo") ) { SCIP_CALL( SCIPsetBoolParam(subscip, "conflict/usepseudo", FALSE) ); } /* add an objective cutoff */ cutoff = SCIPinfinity(scip); assert(!SCIPisInfinity(scip, SCIPgetUpperbound(scip))); upperbound = SCIPgetUpperbound(scip) - SCIPsumepsilon(scip); if( !SCIPisInfinity(scip,-1.0*SCIPgetLowerbound(scip)) ) { cutoff = (1-heurdata->minimprove)*SCIPgetUpperbound(scip) + heurdata->minimprove*SCIPgetLowerbound(scip); } else { if( SCIPgetUpperbound ( scip ) >= 0 ) cutoff = ( 1 - heurdata->minimprove ) * SCIPgetUpperbound ( scip ); else cutoff = ( 1 + heurdata->minimprove ) * SCIPgetUpperbound ( scip ); } cutoff = MIN(upperbound, cutoff ); SCIP_CALL( SCIPsetObjlimit(subscip, cutoff) ); /* permute the subproblem to increase diversification */ if( heurdata->permute ) { SCIP_CALL( SCIPpermuteProb(subscip, (unsigned int) SCIPheurGetNCalls(heur), TRUE, TRUE, TRUE, TRUE, TRUE) ); } /* solve the subproblem */ SCIPdebugMessage("Solve Crossover subMIP\n"); 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(scip, "Error while solving subproblem in Crossover heuristic; sub-SCIP terminated with code <%d>\n", retcode); } heurdata->usednodes += SCIPgetNNodes(subscip); /* check, whether a solution was found */ if( SCIPgetNSols(subscip) > 0 ) { SCIP_SOL** subsols; int nsubsols; int solindex; /* index of the solution created by crossover */ /* check, whether a solution was found; * due to numerics, it might happen that not all solutions are feasible -> try all solutions until one was accepted */ nsubsols = SCIPgetNSols(subscip); subsols = SCIPgetSols(subscip); success = FALSE; solindex = -1; for( i = 0; i < nsubsols && !success; ++i ) { SCIP_CALL( createNewSol(scip, subscip, subvars, heur, subsols[i], &solindex, &success) ); } if( success ) { int tmp; assert(solindex != -1); *result = SCIP_FOUNDSOL; /* insert all crossings of the new solution and (nusedsols-1) of its parents into the hashtable * in order to avoid incest ;) */ for( i = 0; i < nusedsols; i++ ) { SOLTUPLE* elem; tmp = selection[i]; selection[i] = solindex; SCIP_CALL( createSolTuple(scip, &elem, selection, nusedsols, heurdata) ); SCIP_CALL( SCIPhashtableInsert(heurdata->hashtable, elem) ); selection[i] = tmp; } /* if solution was among the best ones, crossover should not be called until another good solution was found */ if( !heurdata->randomization ) { heurdata->prevbestsol = SCIPgetBestSol(scip); heurdata->prevlastsol = SCIPgetSols(scip)[heurdata->nusedsols-1]; } } /* if solution is not better then incumbent or could not be added to problem => run is counted as a failure */ if( !success || solindex != SCIPsolGetIndex(SCIPgetBestSol(scip)) ) updateFailureStatistic(scip, heurdata); } else { /* if no new solution was found, run was a failure */ updateFailureStatistic(scip, heurdata); } TERMINATE: /* free subproblem */ SCIPfreeBufferArray(scip, &selection); SCIPfreeBufferArray(scip, &subvars); SCIP_CALL( SCIPfree(&subscip) ); 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; }