/** 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(heurExecFixandinfer) { /*lint --e{715}*/ SCIP_HEURDATA* heurdata; SCIP_VAR** cands; int ncands; int startncands; int divedepth; SCIP_Bool cutoff; SCIP_Real large; *result = SCIP_DIDNOTRUN; /* we cannot run on problems with continuous variables */ if( SCIPgetNContVars(scip) > 0 ) return SCIP_OKAY; /* get unfixed variables */ SCIP_CALL( SCIPgetPseudoBranchCands(scip, &cands, &ncands, NULL) ); if( ncands == 0 ) return SCIP_OKAY; SCIPdebugMessage("starting fix-and-infer heuristic with %d unfixed integral variables\n", ncands); *result = SCIP_DIDNOTFIND; /* get heuristic data */ heurdata = SCIPheurGetData(heur); assert(heurdata != NULL); /* start probing */ SCIP_CALL( SCIPstartProbing(scip) ); /* fix variables and propagate inferences as long as the problem is still feasible and there are * unfixed integral variables */ cutoff = FALSE; divedepth = 0; startncands = ncands; /* determine large value to set variables to */ large = SCIPinfinity(scip); if( !SCIPisInfinity(scip, 0.1 / SCIPfeastol(scip)) ) large = 0.1 / SCIPfeastol(scip); while( !cutoff && ncands > 0 && (divedepth < heurdata->minfixings || (startncands - ncands) * 2 * MAXDIVEDEPTH >= startncands * divedepth) && !SCIPisStopped(scip) ) { divedepth++; /* create next probing node */ SCIP_CALL( SCIPnewProbingNode(scip) ); /* fix next variable */ SCIP_CALL( fixVariable(scip, cands, ncands, large) ); /* propagate the fixing */ SCIP_CALL( SCIPpropagateProbing(scip, heurdata->proprounds, &cutoff, NULL) ); /* get remaining unfixed variables */ if( !cutoff ) { SCIP_CALL( SCIPgetPseudoBranchCands(scip, &cands, &ncands, NULL) ); } } /* check, if we are still feasible */ if( cutoff ) { SCIPdebugMessage("propagation detected a cutoff\n"); } else if( ncands == 0 ) { SCIP_Bool success; success = FALSE; /* try to add solution to SCIP */ SCIP_CALL( SCIPtryCurrentSol(scip, heur, FALSE, FALSE, TRUE, &success) ); if( success ) { SCIPdebugMessage("found primal feasible solution\n"); *result = SCIP_FOUNDSOL; } else { SCIPdebugMessage("primal solution was rejected\n"); } } else { SCIPdebugMessage("probing was aborted (probing depth: %d, fixed: %d/%d)", divedepth, startncands - ncands, startncands); } /* end probing */ SCIP_CALL( SCIPendProbing(scip) ); return SCIP_OKAY; }
/** 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; }
/** 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; }