/** creates the rows of the subproblem */ static SCIP_RETCODE createRows( SCIP* scip, /**< original SCIP data structure */ SCIP* subscip, /**< SCIP data structure for the subproblem */ SCIP_VAR** subvars /**< the variables of the subproblem */ ) { SCIP_ROW** rows; /* original scip rows */ SCIP_CONS* cons; /* new constraint */ SCIP_VAR** consvars; /* new constraint's variables */ SCIP_COL** cols; /* original row's columns */ SCIP_Real constant; /* constant added to the row */ SCIP_Real lhs; /* left hand side of the row */ SCIP_Real rhs; /* left right side of the row */ SCIP_Real* vals; /* variables' coefficient values of the row */ int nrows; int nnonz; int i; int j; /* get the rows and their number */ SCIP_CALL( SCIPgetLPRowsData(scip, &rows, &nrows) ); /* copy all rows to linear constraints */ for( i = 0; i < nrows; i++ ) { /* ignore rows that are only locally valid */ if( SCIProwIsLocal(rows[i]) ) continue; /* get the row's data */ constant = SCIProwGetConstant(rows[i]); lhs = SCIProwGetLhs(rows[i]) - constant; rhs = SCIProwGetRhs(rows[i]) - constant; vals = SCIProwGetVals(rows[i]); nnonz = SCIProwGetNNonz(rows[i]); cols = SCIProwGetCols(rows[i]); assert(lhs <= rhs); /* allocate memory array to be filled with the corresponding subproblem variables */ SCIP_CALL( SCIPallocBufferArray(scip, &consvars, nnonz) ); for( j = 0; j < nnonz; j++ ) consvars[j] = subvars[SCIPvarGetProbindex(SCIPcolGetVar(cols[j]))]; /* create a new linear constraint and add it to the subproblem */ SCIP_CALL( SCIPcreateConsLinear(subscip, &cons, SCIProwGetName(rows[i]), nnonz, consvars, vals, lhs, rhs, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE) ); SCIP_CALL( SCIPaddCons(subscip, cons) ); SCIP_CALL( SCIPreleaseCons(subscip, &cons) ); /* free temporary memory */ SCIPfreeBufferArray(scip, &consvars); } return SCIP_OKAY; }
/** creates a subproblem for subscip by fixing a number of variables */ static SCIP_RETCODE createSubproblem( SCIP* scip, /**< original SCIP data structure */ SCIP* subscip, /**< SCIP data structure for the subproblem */ SCIP_VAR** subvars, /**< the variables of the subproblem */ SCIP_Real minfixingrate, /**< percentage of integer variables that have to be fixed */ unsigned int* randseed, /**< a seed value for the random number generator */ SCIP_Bool uselprows /**< should subproblem be created out of the rows in the LP rows? */ ) { SCIP_VAR** vars; /* original scip variables */ SCIP_SOL* sol; /* pool of solutions */ SCIP_Bool* marked; /* array of markers, which variables to fixed */ SCIP_Bool fixingmarker; /* which flag should label a fixed variable? */ int nvars; int nbinvars; int nintvars; int i; int j; int nmarkers; /* get required data of the original problem */ SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, &nbinvars, &nintvars, NULL, NULL) ); sol = SCIPgetBestSol(scip); assert(sol != NULL); SCIP_CALL( SCIPallocBufferArray(scip, &marked, nbinvars+nintvars) ); if( minfixingrate > 0.5 ) { nmarkers = nbinvars + nintvars - (int) SCIPfloor(scip, minfixingrate*(nbinvars+nintvars)); fixingmarker = FALSE; } else { nmarkers = (int) SCIPceil(scip, minfixingrate*(nbinvars+nintvars)); fixingmarker = TRUE; } assert( 0 <= nmarkers && nmarkers <= SCIPceil(scip,(nbinvars+nintvars)/2.0 ) ); j = 0; BMSclearMemoryArray(marked, nbinvars+nintvars); while( j < nmarkers ) { do { i = SCIPgetRandomInt(0, nbinvars+nintvars-1, randseed); } while( marked[i] ); marked[i] = TRUE; j++; } assert( j == nmarkers ); /* change bounds of variables of the subproblem */ for( i = 0; i < nbinvars + nintvars; i++ ) { /* fix all randomly marked variables */ if( marked[i] == fixingmarker ) { SCIP_Real solval; SCIP_Real lb; SCIP_Real ub; solval = SCIPgetSolVal(scip, sol, vars[i]); lb = SCIPvarGetLbGlobal(subvars[i]); ub = SCIPvarGetUbGlobal(subvars[i]); assert(SCIPisLE(scip, lb, ub)); /* due to dual reductions, it may happen that the solution value is not in the variable's domain anymore */ if( SCIPisLT(scip, solval, lb) ) solval = lb; else if( SCIPisGT(scip, solval, ub) ) solval = ub; /* perform the bound change */ if( !SCIPisInfinity(scip, solval) && !SCIPisInfinity(scip, -solval) ) { SCIP_CALL( SCIPchgVarLbGlobal(subscip, subvars[i], solval) ); SCIP_CALL( SCIPchgVarUbGlobal(subscip, subvars[i], solval) ); } } } if( uselprows ) { SCIP_ROW** rows; /* original scip rows */ int nrows; /* get the rows and their number */ SCIP_CALL( SCIPgetLPRowsData(scip, &rows, &nrows) ); /* copy all rows to linear constraints */ for( i = 0; i < nrows; i++ ) { SCIP_CONS* cons; SCIP_VAR** consvars; SCIP_COL** cols; SCIP_Real constant; SCIP_Real lhs; SCIP_Real rhs; SCIP_Real* vals; int nnonz; /* ignore rows that are only locally valid */ if( SCIProwIsLocal(rows[i]) ) continue; /* get the row's data */ constant = SCIProwGetConstant(rows[i]); lhs = SCIProwGetLhs(rows[i]) - constant; rhs = SCIProwGetRhs(rows[i]) - constant; vals = SCIProwGetVals(rows[i]); nnonz = SCIProwGetNNonz(rows[i]); cols = SCIProwGetCols(rows[i]); assert( lhs <= rhs ); /* allocate memory array to be filled with the corresponding subproblem variables */ SCIP_CALL( SCIPallocBufferArray(scip, &consvars, nnonz) ); for( j = 0; j < nnonz; j++ ) consvars[j] = subvars[SCIPvarGetProbindex(SCIPcolGetVar(cols[j]))]; /* create a new linear constraint and add it to the subproblem */ SCIP_CALL( SCIPcreateConsLinear(subscip, &cons, SCIProwGetName(rows[i]), nnonz, consvars, vals, lhs, rhs, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE) ); SCIP_CALL( SCIPaddCons(subscip, cons) ); SCIP_CALL( SCIPreleaseCons(subscip, &cons) ); /* free temporary memory */ SCIPfreeBufferArray(scip, &consvars); } } SCIPfreeBufferArray(scip, &marked); return SCIP_OKAY; }
/** execution method of primal heuristic */ static SCIP_DECL_HEUREXEC(heurExecOneopt) { /*lint --e{715}*/ SCIP_HEURDATA* heurdata; SCIP_SOL* bestsol; /* incumbent solution */ SCIP_SOL* worksol; /* heuristic's working solution */ SCIP_VAR** vars; /* SCIP variables */ SCIP_VAR** shiftcands; /* shiftable variables */ SCIP_ROW** lprows; /* SCIP LP rows */ SCIP_Real* activities; /* row activities for working solution */ SCIP_Real* shiftvals; SCIP_Real lb; SCIP_Real ub; SCIP_Bool localrows; SCIP_Bool valid; int nchgbound; int nbinvars; int nintvars; int nvars; int nlprows; int i; int nshiftcands; int shiftcandssize; 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; /* we only want to process each solution once */ bestsol = SCIPgetBestSol(scip); if( bestsol == NULL || heurdata->lastsolindex == SCIPsolGetIndex(bestsol) ) return SCIP_OKAY; /* reset the timing mask to its default value (at the root node it could be different) */ if( SCIPgetNNodes(scip) > 1 ) SCIPheurSetTimingmask(heur, HEUR_TIMING); /* get problem variables */ SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, &nbinvars, &nintvars, NULL, NULL) ); nintvars += nbinvars; /* do not run if there are no discrete variables */ if( nintvars == 0 ) { *result = SCIP_DIDNOTRUN; return SCIP_OKAY; } if( heurtiming == SCIP_HEURTIMING_BEFOREPRESOL ) { SCIP* subscip; /* the subproblem created by zeroobj */ SCIP_HASHMAP* varmapfw; /* mapping of SCIP variables to sub-SCIP variables */ SCIP_VAR** subvars; /* subproblem's variables */ SCIP_Real* subsolvals; /* solution values of the subproblem */ SCIP_Real timelimit; /* time limit for zeroobj subproblem */ SCIP_Real memorylimit; /* memory limit for zeroobj subproblem */ SCIP_SOL* startsol; SCIP_SOL** subsols; int nsubsols; if( !heurdata->beforepresol ) 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; /* 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) ); /* copy complete SCIP instance */ valid = FALSE; SCIP_CALL( SCIPcopy(scip, subscip, varmapfw, NULL, "oneopt", TRUE, FALSE, TRUE, &valid) ); SCIP_CALL( SCIPtransformProb(subscip) ); /* get variable image */ for( i = 0; i < nvars; i++ ) subvars[i] = (SCIP_VAR*) SCIPhashmapGetImage(varmapfw, vars[i]); /* copy the solution */ SCIP_CALL( SCIPallocBufferArray(scip, &subsolvals, nvars) ); SCIP_CALL( SCIPgetSolVals(scip, bestsol, nvars, vars, subsolvals) ); /* create start solution for the subproblem */ SCIP_CALL( SCIPcreateOrigSol(subscip, &startsol, NULL) ); SCIP_CALL( SCIPsetSolVals(subscip, startsol, nvars, subvars, subsolvals) ); /* try to add new solution to sub-SCIP and free it immediately */ valid = FALSE; SCIP_CALL( SCIPtrySolFree(subscip, &startsol, FALSE, FALSE, FALSE, FALSE, &valid) ); SCIPfreeBufferArray(scip, &subsolvals); SCIPhashmapFree(&varmapfw); /* disable statistic timing inside sub SCIP */ SCIP_CALL( SCIPsetBoolParam(subscip, "timing/statistictiming", FALSE) ); /* deactivate basically everything except oneopt in the sub-SCIP */ SCIP_CALL( SCIPsetPresolving(subscip, SCIP_PARAMSETTING_OFF, TRUE) ); SCIP_CALL( SCIPsetHeuristics(subscip, SCIP_PARAMSETTING_OFF, TRUE) ); SCIP_CALL( SCIPsetSeparating(subscip, SCIP_PARAMSETTING_OFF, TRUE) ); SCIP_CALL( SCIPsetLongintParam(subscip, "limits/nodes", 1LL) ); SCIP_CALL( SCIPsetRealParam(subscip, "limits/time", timelimit) ); SCIP_CALL( SCIPsetRealParam(subscip, "limits/memory", memorylimit) ); SCIP_CALL( SCIPsetBoolParam(subscip, "misc/catchctrlc", FALSE) ); SCIP_CALL( SCIPsetIntParam(subscip, "display/verblevel", 0) ); /* if necessary, some of the parameters have to be unfixed first */ if( SCIPisParamFixed(subscip, "lp/solvefreq") ) { SCIPwarningMessage(scip, "unfixing parameter lp/solvefreq in subscip of oneopt heuristic\n"); SCIP_CALL( SCIPunfixParam(subscip, "lp/solvefreq") ); } SCIP_CALL( SCIPsetIntParam(subscip, "lp/solvefreq", -1) ); if( SCIPisParamFixed(subscip, "heuristics/oneopt/freq") ) { SCIPwarningMessage(scip, "unfixing parameter heuristics/oneopt/freq in subscip of oneopt heuristic\n"); SCIP_CALL( SCIPunfixParam(subscip, "heuristics/oneopt/freq") ); } SCIP_CALL( SCIPsetIntParam(subscip, "heuristics/oneopt/freq", 1) ); if( SCIPisParamFixed(subscip, "heuristics/oneopt/forcelpconstruction") ) { SCIPwarningMessage(scip, "unfixing parameter heuristics/oneopt/forcelpconstruction in subscip of oneopt heuristic\n"); SCIP_CALL( SCIPunfixParam(subscip, "heuristics/oneopt/forcelpconstruction") ); } SCIP_CALL( SCIPsetBoolParam(subscip, "heuristics/oneopt/forcelpconstruction", TRUE) ); /* avoid recursive call, which would lead to an endless loop */ if( SCIPisParamFixed(subscip, "heuristics/oneopt/beforepresol") ) { SCIPwarningMessage(scip, "unfixing parameter heuristics/oneopt/beforepresol in subscip of oneopt heuristic\n"); SCIP_CALL( SCIPunfixParam(subscip, "heuristics/oneopt/beforepresol") ); } SCIP_CALL( SCIPsetBoolParam(subscip, "heuristics/oneopt/beforepresol", FALSE) ); if( valid ) { 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 zeroobj heuristic; sub-SCIP terminated with code <%d>\n",retcode); } #ifdef SCIP_DEBUG SCIP_CALL( SCIPprintStatistics(subscip, NULL) ); #endif } /* 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); valid = FALSE; for( i = 0; i < nsubsols && !valid; ++i ) { SCIP_CALL( createNewSol(scip, subscip, subvars, heur, subsols[i], &valid) ); if( valid ) *result = SCIP_FOUNDSOL; } /* free subproblem */ SCIPfreeBufferArray(scip, &subvars); SCIP_CALL( SCIPfree(&subscip) ); return SCIP_OKAY; } /* we can only work on solutions valid in the transformed space */ if( SCIPsolIsOriginal(bestsol) ) return SCIP_OKAY; if( heurtiming == SCIP_HEURTIMING_BEFORENODE && (SCIPhasCurrentNodeLP(scip) || heurdata->forcelpconstruction) ) { SCIP_Bool cutoff; cutoff = FALSE; SCIP_CALL( SCIPconstructLP(scip, &cutoff) ); SCIP_CALL( SCIPflushLP(scip) ); /* get problem variables again, SCIPconstructLP() might have added new variables */ SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, &nbinvars, &nintvars, NULL, NULL) ); nintvars += nbinvars; } /* we need an LP */ if( SCIPgetNLPRows(scip) == 0 ) return SCIP_OKAY; *result = SCIP_DIDNOTFIND; nchgbound = 0; /* initialize data */ nshiftcands = 0; shiftcandssize = 8; heurdata->lastsolindex = SCIPsolGetIndex(bestsol); SCIP_CALL( SCIPcreateSolCopy(scip, &worksol, bestsol) ); SCIPsolSetHeur(worksol,heur); SCIPdebugMessage("Starting bound adjustment in 1-opt heuristic\n"); /* maybe change solution values due to global bound changes first */ for( i = nvars - 1; i >= 0; --i ) { SCIP_VAR* var; SCIP_Real solval; var = vars[i]; lb = SCIPvarGetLbGlobal(var); ub = SCIPvarGetUbGlobal(var); solval = SCIPgetSolVal(scip, bestsol,var); /* old solution value is smaller than the actual lower bound */ if( SCIPisFeasLT(scip, solval, lb) ) { /* set the solution value to the global lower bound */ SCIP_CALL( SCIPsetSolVal(scip, worksol, var, lb) ); ++nchgbound; SCIPdebugMessage("var <%s> type %d, old solval %g now fixed to lb %g\n", SCIPvarGetName(var), SCIPvarGetType(var), solval, lb); } /* old solution value is greater than the actual upper bound */ else if( SCIPisFeasGT(scip, solval, SCIPvarGetUbGlobal(var)) ) { /* set the solution value to the global upper bound */ SCIP_CALL( SCIPsetSolVal(scip, worksol, var, ub) ); ++nchgbound; SCIPdebugMessage("var <%s> type %d, old solval %g now fixed to ub %g\n", SCIPvarGetName(var), SCIPvarGetType(var), solval, ub); } } SCIPdebugMessage("number of bound changes (due to global bounds) = %d\n", nchgbound); SCIP_CALL( SCIPgetLPRowsData(scip, &lprows, &nlprows) ); SCIP_CALL( SCIPallocBufferArray(scip, &activities, nlprows) ); localrows = FALSE; valid = TRUE; /* initialize activities */ for( i = 0; i < nlprows; ++i ) { SCIP_ROW* row; row = lprows[i]; assert(SCIProwGetLPPos(row) == i); if( !SCIProwIsLocal(row) ) { activities[i] = SCIPgetRowSolActivity(scip, row, worksol); SCIPdebugMessage("Row <%s> has activity %g\n", SCIProwGetName(row), activities[i]); if( SCIPisFeasLT(scip, activities[i], SCIProwGetLhs(row)) || SCIPisFeasGT(scip, activities[i], SCIProwGetRhs(row)) ) { valid = FALSE; SCIPdebug( SCIP_CALL( SCIPprintRow(scip, row, NULL) ) ); SCIPdebugMessage("row <%s> activity %g violates bounds, lhs = %g, rhs = %g\n", SCIProwGetName(row), activities[i], SCIProwGetLhs(row), SCIProwGetRhs(row)); break; } } else localrows = TRUE; } if( !valid ) { /** @todo try to correct lp rows */ SCIPdebugMessage("Some global bound changes were not valid in lp rows.\n"); goto TERMINATE; } SCIP_CALL( SCIPallocBufferArray(scip, &shiftcands, shiftcandssize) ); SCIP_CALL( SCIPallocBufferArray(scip, &shiftvals, shiftcandssize) ); SCIPdebugMessage("Starting 1-opt heuristic\n"); /* enumerate all integer variables and find out which of them are shiftable */ for( i = 0; i < nintvars; i++ ) { if( SCIPvarGetStatus(vars[i]) == SCIP_VARSTATUS_COLUMN ) { SCIP_Real shiftval; SCIP_Real solval; /* find out whether the variable can be shifted */ solval = SCIPgetSolVal(scip, worksol, vars[i]); shiftval = calcShiftVal(scip, vars[i], solval, activities); /* insert the variable into the list of shifting candidates */ if( !SCIPisFeasZero(scip, shiftval) ) { SCIPdebugMessage(" -> Variable <%s> can be shifted by <%1.1f> \n", SCIPvarGetName(vars[i]), shiftval); if( nshiftcands == shiftcandssize) { shiftcandssize *= 8; SCIP_CALL( SCIPreallocBufferArray(scip, &shiftcands, shiftcandssize) ); SCIP_CALL( SCIPreallocBufferArray(scip, &shiftvals, shiftcandssize) ); } shiftcands[nshiftcands] = vars[i]; shiftvals[nshiftcands] = shiftval; nshiftcands++; } } } /* if at least one variable can be shifted, shift variables sorted by their objective */ if( nshiftcands > 0 ) { SCIP_Real shiftval; SCIP_Real solval; SCIP_VAR* var; /* the case that exactly one variable can be shifted is slightly easier */ if( nshiftcands == 1 ) { var = shiftcands[0]; assert(var != NULL); solval = SCIPgetSolVal(scip, worksol, var); shiftval = shiftvals[0]; assert(!SCIPisFeasZero(scip,shiftval)); SCIPdebugMessage(" Only one shiftcand found, var <%s>, which is now shifted by<%1.1f> \n", SCIPvarGetName(var), shiftval); SCIP_CALL( SCIPsetSolVal(scip, worksol, var, solval+shiftval) ); } else { SCIP_Real* objcoeffs; SCIP_CALL( SCIPallocBufferArray(scip, &objcoeffs, nshiftcands) ); SCIPdebugMessage(" %d shiftcands found \n", nshiftcands); /* sort the variables by their objective, optionally weighted with the shiftval */ if( heurdata->weightedobj ) { for( i = 0; i < nshiftcands; ++i ) objcoeffs[i] = SCIPvarGetObj(shiftcands[i])*shiftvals[i]; } else { for( i = 0; i < nshiftcands; ++i ) objcoeffs[i] = SCIPvarGetObj(shiftcands[i]); } /* sort arrays with respect to the first one */ SCIPsortRealPtr(objcoeffs, (void**)shiftcands, nshiftcands); /* try to shift each variable -> Activities have to be updated */ for( i = 0; i < nshiftcands; ++i ) { var = shiftcands[i]; assert(var != NULL); solval = SCIPgetSolVal(scip, worksol, var); shiftval = calcShiftVal(scip, var, solval, activities); SCIPdebugMessage(" -> Variable <%s> is now shifted by <%1.1f> \n", SCIPvarGetName(vars[i]), shiftval); assert(i > 0 || !SCIPisFeasZero(scip, shiftval)); assert(SCIPisFeasGE(scip, solval+shiftval, SCIPvarGetLbGlobal(var)) && SCIPisFeasLE(scip, solval+shiftval, SCIPvarGetUbGlobal(var))); SCIP_CALL( SCIPsetSolVal(scip, worksol, var, solval+shiftval) ); SCIP_CALL( updateRowActivities(scip, activities, var, shiftval) ); } SCIPfreeBufferArray(scip, &objcoeffs); } /* if the problem is a pure IP, try to install the solution, if it is a MIP, solve LP again to set the continuous * variables to the best possible value */ if( nvars == nintvars || !SCIPhasCurrentNodeLP(scip) || SCIPgetLPSolstat(scip) != SCIP_LPSOLSTAT_OPTIMAL ) { SCIP_Bool success; /* since we ignore local rows, we cannot guarantee their feasibility and have to set the checklprows flag to * TRUE if local rows are present */ SCIP_CALL( SCIPtrySol(scip, worksol, FALSE, FALSE, FALSE, localrows, &success) ); if( success ) { SCIPdebugMessage("found feasible shifted solution:\n"); SCIPdebug( SCIP_CALL( SCIPprintSol(scip, worksol, NULL, FALSE) ) ); heurdata->lastsolindex = SCIPsolGetIndex(bestsol); *result = SCIP_FOUNDSOL; } } else { SCIP_Bool lperror; #ifdef NDEBUG SCIP_RETCODE retstat; #endif SCIPdebugMessage("shifted solution should be feasible -> solve LP to fix continuous variables to best values\n"); /* start diving to calculate the LP relaxation */ SCIP_CALL( SCIPstartDive(scip) ); /* set the bounds of the variables: fixed for integers, global bounds for continuous */ for( i = 0; i < nvars; ++i ) { if( SCIPvarGetStatus(vars[i]) == SCIP_VARSTATUS_COLUMN ) { SCIP_CALL( SCIPchgVarLbDive(scip, vars[i], SCIPvarGetLbGlobal(vars[i])) ); SCIP_CALL( SCIPchgVarUbDive(scip, vars[i], SCIPvarGetUbGlobal(vars[i])) ); } } /* apply this after global bounds to not cause an error with intermediate empty domains */ for( i = 0; i < nintvars; ++i ) { if( SCIPvarGetStatus(vars[i]) == SCIP_VARSTATUS_COLUMN ) { solval = SCIPgetSolVal(scip, worksol, vars[i]); SCIP_CALL( SCIPchgVarLbDive(scip, vars[i], solval) ); SCIP_CALL( SCIPchgVarUbDive(scip, vars[i], solval) ); } } /* solve LP */ SCIPdebugMessage(" -> old LP iterations: %" SCIP_LONGINT_FORMAT "\n", SCIPgetNLPIterations(scip)); /**@todo in case of an MINLP, if SCIPisNLPConstructed() is TRUE, say, rather solve the NLP instead of the LP */ /* Errors in the LP solver should not kill the overall solving process, if the LP is just needed for a heuristic. * Hence in optimized mode, the return code is caught and a warning is printed, only in debug mode, SCIP will stop. */ #ifdef NDEBUG retstat = SCIPsolveDiveLP(scip, -1, &lperror, NULL); if( retstat != SCIP_OKAY ) { SCIPwarningMessage(scip, "Error while solving LP in Oneopt heuristic; LP solve terminated with code <%d>\n",retstat); } #else SCIP_CALL( SCIPsolveDiveLP(scip, -1, &lperror, NULL) ); #endif SCIPdebugMessage(" -> new LP iterations: %" SCIP_LONGINT_FORMAT "\n", SCIPgetNLPIterations(scip)); SCIPdebugMessage(" -> error=%u, status=%d\n", lperror, SCIPgetLPSolstat(scip)); /* check if this is a feasible solution */ if( !lperror && SCIPgetLPSolstat(scip) == SCIP_LPSOLSTAT_OPTIMAL ) { SCIP_Bool success; /* copy the current LP solution to the working solution */ SCIP_CALL( SCIPlinkLPSol(scip, worksol) ); SCIP_CALL( SCIPtrySol(scip, worksol, FALSE, FALSE, FALSE, FALSE, &success) ); /* check solution for feasibility */ if( success ) { SCIPdebugMessage("found feasible shifted solution:\n"); SCIPdebug( SCIP_CALL( SCIPprintSol(scip, worksol, NULL, FALSE) ) ); heurdata->lastsolindex = SCIPsolGetIndex(bestsol); *result = SCIP_FOUNDSOL; } } /* terminate the diving */ SCIP_CALL( SCIPendDive(scip) ); } } SCIPdebugMessage("Finished 1-opt heuristic\n"); SCIPfreeBufferArray(scip, &shiftvals); SCIPfreeBufferArray(scip, &shiftcands); TERMINATE: SCIPfreeBufferArray(scip, &activities); SCIP_CALL( SCIPfreeSol(scip, &worksol) ); return SCIP_OKAY; }
/** creates a subproblem for subscip by fixing a number of variables */ static SCIP_RETCODE createSubproblem( SCIP* scip, /**< original SCIP data structure */ SCIP* subscip, /**< SCIP data structure for the subproblem */ SCIP_VAR** subvars, /**< the variables of the subproblem */ SCIP_Real minfixingrate, /**< percentage of integer variables that have to be fixed */ 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_Bool* success /**< pointer to store whether the problem was created successfully */ ) { SCIP_VAR** vars; /* original SCIP variables */ SCIP_Real fixingrate; int nvars; int nbinvars; int nintvars; int i; int fixingcounter; assert(scip != NULL); assert(subscip != NULL); assert(subvars != NULL); assert(0.0 <= minfixingrate && minfixingrate <= 1.0); /* get required variable data */ SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, &nbinvars, &nintvars, NULL, NULL) ); fixingcounter = 0; /* change bounds of variables of the subproblem */ for( i = 0; i < nbinvars + nintvars; i++ ) { SCIP_Real lpsolval; SCIP_Real lb; SCIP_Real ub; /* get the current LP solution for each variable */ lpsolval = SCIPgetRelaxSolVal(scip, vars[i]); if( SCIPisFeasIntegral(scip, lpsolval) ) { /* fix variables to current LP solution if it is integral, * use exact integral value, if the variable is only integral within numerical tolerances */ lb = SCIPfloor(scip, lpsolval+0.5); ub = lb; fixingcounter++; } else if( binarybounds ) { /* if the sub problem should be a binary problem, change the bounds to nearest integers */ lb = SCIPfeasFloor(scip,lpsolval); ub = SCIPfeasCeil(scip,lpsolval); } else { /* otherwise just copy bounds */ lb = SCIPvarGetLbGlobal(vars[i]); ub = SCIPvarGetUbGlobal(vars[i]); } /* perform the bound change */ SCIP_CALL( SCIPchgVarLbGlobal(subscip, subvars[i], lb) ); SCIP_CALL( SCIPchgVarUbGlobal(subscip, subvars[i], ub) ); } /* abort, if all integer variables were fixed (which should not happen for MIP) */ if( fixingcounter == nbinvars + nintvars ) { *success = FALSE; return SCIP_OKAY; } else fixingrate = fixingcounter / (SCIP_Real)(MAX(nbinvars + nintvars, 1)); SCIPdebugMessage("fixing rate: %g = %d of %d\n", fixingrate, fixingcounter, nbinvars + nintvars); /* abort, if the amount of fixed variables is insufficient */ if( fixingrate < minfixingrate ) { *success = FALSE; return SCIP_OKAY; } if( uselprows ) { SCIP_ROW** rows; /* original scip rows */ int nrows; /* get the rows and their number */ SCIP_CALL( SCIPgetLPRowsData(scip, &rows, &nrows) ); /* copy all rows to linear constraints */ for( i = 0; i < nrows; i++ ) { SCIP_CONS* cons; SCIP_VAR** consvars; SCIP_COL** cols; SCIP_Real constant; SCIP_Real lhs; SCIP_Real rhs; SCIP_Real* vals; int nnonz; int j; /* ignore rows that are only locally valid */ if( SCIProwIsLocal(rows[i]) ) continue; /* get the row's data */ constant = SCIProwGetConstant(rows[i]); lhs = SCIProwGetLhs(rows[i]) - constant; rhs = SCIProwGetRhs(rows[i]) - constant; vals = SCIProwGetVals(rows[i]); nnonz = SCIProwGetNNonz(rows[i]); cols = SCIProwGetCols(rows[i]); assert( lhs <= rhs ); /* allocate memory array to be filled with the corresponding subproblem variables */ SCIP_CALL( SCIPallocBufferArray(subscip, &consvars, nnonz) ); for( j = 0; j < nnonz; j++ ) consvars[j] = subvars[SCIPvarGetProbindex(SCIPcolGetVar(cols[j]))]; /* create a new linear constraint and add it to the subproblem */ SCIP_CALL( SCIPcreateConsLinear(subscip, &cons, SCIProwGetName(rows[i]), nnonz, consvars, vals, lhs, rhs, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE) ); SCIP_CALL( SCIPaddCons(subscip, cons) ); SCIP_CALL( SCIPreleaseCons(subscip, &cons) ); /* free temporary memory */ SCIPfreeBufferArray(subscip, &consvars); } } *success = TRUE; return SCIP_OKAY; }
/** LP solution separation method of separator */ static SCIP_DECL_SEPAEXECLP(sepaExeclpGomory) { /*lint --e{715}*/ SCIP_SEPADATA* sepadata; SCIP_VAR** vars; SCIP_COL** cols; SCIP_ROW** rows; SCIP_Real* binvrow; SCIP_Real* cutcoefs; SCIP_Real maxscale; SCIP_Real minfrac; SCIP_Real maxfrac; SCIP_Longint maxdnom; SCIP_Bool cutoff; int* basisind; int naddedcuts; int nvars; int ncols; int nrows; int ncalls; int depth; int maxdepth; int maxsepacuts; int c; int i; assert(sepa != NULL); assert(strcmp(SCIPsepaGetName(sepa), SEPA_NAME) == 0); assert(scip != NULL); assert(result != NULL); *result = SCIP_DIDNOTRUN; sepadata = SCIPsepaGetData(sepa); assert(sepadata != NULL); depth = SCIPgetDepth(scip); ncalls = SCIPsepaGetNCallsAtNode(sepa); minfrac = sepadata->away; maxfrac = 1.0 - sepadata->away; /* only call separator, if we are not close to terminating */ if( SCIPisStopped(scip) ) return SCIP_OKAY; /* only call the gomory cut separator a given number of times at each node */ if( (depth == 0 && sepadata->maxroundsroot >= 0 && ncalls >= sepadata->maxroundsroot) || (depth > 0 && sepadata->maxrounds >= 0 && ncalls >= sepadata->maxrounds) ) return SCIP_OKAY; /* only call separator, if an optimal LP solution is at hand */ if( SCIPgetLPSolstat(scip) != SCIP_LPSOLSTAT_OPTIMAL ) return SCIP_OKAY; /* only call separator, if the LP solution is basic */ if( !SCIPisLPSolBasic(scip) ) return SCIP_OKAY; /* only call separator, if there are fractional variables */ if( SCIPgetNLPBranchCands(scip) == 0 ) return SCIP_OKAY; /* get variables data */ SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL) ); /* get LP data */ SCIP_CALL( SCIPgetLPColsData(scip, &cols, &ncols) ); SCIP_CALL( SCIPgetLPRowsData(scip, &rows, &nrows) ); if( ncols == 0 || nrows == 0 ) return SCIP_OKAY; #if 0 /* if too many columns, separator is usually very slow: delay it until no other cuts have been found */ if( ncols >= 50*nrows ) return SCIP_OKAY; if( ncols >= 5*nrows ) { int ncutsfound; ncutsfound = SCIPgetNCutsFound(scip); if( ncutsfound > sepadata->lastncutsfound || !SCIPsepaWasLPDelayed(sepa) ) { sepadata->lastncutsfound = ncutsfound; *result = SCIP_DELAYED; return SCIP_OKAY; } } #endif /* set the maximal denominator in rational representation of gomory cut and the maximal scale factor to * scale resulting cut to integral values to avoid numerical instabilities */ /**@todo find better but still stable gomory cut settings: look at dcmulti, gesa3, khb0525, misc06, p2756 */ maxdepth = SCIPgetMaxDepth(scip); if( depth == 0 ) { maxdnom = 1000; maxscale = 1000.0; } else if( depth <= maxdepth/4 ) { maxdnom = 1000; maxscale = 1000.0; } else if( depth <= maxdepth/2 ) { maxdnom = 100; maxscale = 100.0; } else { maxdnom = 10; maxscale = 10.0; } /* allocate temporary memory */ SCIP_CALL( SCIPallocBufferArray(scip, &cutcoefs, nvars) ); SCIP_CALL( SCIPallocBufferArray(scip, &basisind, nrows) ); SCIP_CALL( SCIPallocBufferArray(scip, &binvrow, nrows) ); /* get basis indices */ SCIP_CALL( SCIPgetLPBasisInd(scip, basisind) ); /* get the maximal number of cuts allowed in a separation round */ if( depth == 0 ) maxsepacuts = sepadata->maxsepacutsroot; else maxsepacuts = sepadata->maxsepacuts; SCIPdebugMessage("searching gomory cuts: %d cols, %d rows, maxdnom=%"SCIP_LONGINT_FORMAT", maxscale=%g, maxcuts=%d\n", ncols, nrows, maxdnom, maxscale, maxsepacuts); cutoff = FALSE; naddedcuts = 0; /* for all basic columns belonging to integer variables, try to generate a gomory cut */ for( i = 0; i < nrows && naddedcuts < maxsepacuts && !SCIPisStopped(scip) && !cutoff; ++i ) { SCIP_Bool tryrow; tryrow = FALSE; c = basisind[i]; if( c >= 0 ) { SCIP_VAR* var; assert(c < ncols); var = SCIPcolGetVar(cols[c]); if( SCIPvarGetType(var) != SCIP_VARTYPE_CONTINUOUS ) { SCIP_Real primsol; primsol = SCIPcolGetPrimsol(cols[c]); assert(SCIPgetVarSol(scip, var) == primsol); /*lint !e777*/ if( SCIPfeasFrac(scip, primsol) >= minfrac ) { SCIPdebugMessage("trying gomory cut for col <%s> [%g]\n", SCIPvarGetName(var), primsol); tryrow = TRUE; } } } else if( sepadata->separaterows ) { SCIP_ROW* row; assert(0 <= -c-1 && -c-1 < nrows); row = rows[-c-1]; if( SCIProwIsIntegral(row) && !SCIProwIsModifiable(row) ) { SCIP_Real primsol; primsol = SCIPgetRowActivity(scip, row); if( SCIPfeasFrac(scip, primsol) >= minfrac ) { SCIPdebugMessage("trying gomory cut for row <%s> [%g]\n", SCIProwGetName(row), primsol); tryrow = TRUE; } } } if( tryrow ) { SCIP_Real cutrhs; SCIP_Real cutact; SCIP_Bool success; SCIP_Bool cutislocal; /* get the row of B^-1 for this basic integer variable with fractional solution value */ SCIP_CALL( SCIPgetLPBInvRow(scip, i, binvrow) ); cutact = 0.0; cutrhs = SCIPinfinity(scip); /* create a MIR cut out of the weighted LP rows using the B^-1 row as weights */ SCIP_CALL( SCIPcalcMIR(scip, NULL, BOUNDSWITCH, USEVBDS, ALLOWLOCAL, FIXINTEGRALRHS, NULL, NULL, (int) MAXAGGRLEN(nvars), sepadata->maxweightrange, minfrac, maxfrac, binvrow, 1.0, NULL, NULL, cutcoefs, &cutrhs, &cutact, &success, &cutislocal) ); assert(ALLOWLOCAL || !cutislocal); /* @todo Currently we are using the SCIPcalcMIR() function to compute the coefficients of the Gomory * cut. Alternatively, we could use the direct version (see thesis of Achterberg formula (8.4)) which * leads to cut a of the form \sum a_i x_i \geq 1. Rumor has it that these cuts are better. */ SCIPdebugMessage(" -> success=%u: %g <= %g\n", success, cutact, cutrhs); /* if successful, convert dense cut into sparse row, and add the row as a cut */ if( success && SCIPisFeasGT(scip, cutact, cutrhs) ) { SCIP_ROW* cut; char cutname[SCIP_MAXSTRLEN]; int v; /* construct cut name */ if( c >= 0 ) (void) SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "gom%d_x%d", SCIPgetNLPs(scip), c); else (void) SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "gom%d_s%d", SCIPgetNLPs(scip), -c-1); /* create empty cut */ SCIP_CALL( SCIPcreateEmptyRowSepa(scip, &cut, sepa, cutname, -SCIPinfinity(scip), cutrhs, cutislocal, FALSE, sepadata->dynamiccuts) ); /* cache the row extension and only flush them if the cut gets added */ SCIP_CALL( SCIPcacheRowExtensions(scip, cut) ); /* collect all non-zero coefficients */ for( v = 0; v < nvars; ++v ) { if( !SCIPisZero(scip, cutcoefs[v]) ) { SCIP_CALL( SCIPaddVarToRow(scip, cut, vars[v], cutcoefs[v]) ); } } if( SCIProwGetNNonz(cut) == 0 ) { assert(SCIPisFeasNegative(scip, cutrhs)); SCIPdebugMessage(" -> gomory cut detected infeasibility with cut 0 <= %f\n", cutrhs); cutoff = TRUE; } else if( SCIProwGetNNonz(cut) == 1 ) { /* add the bound change as cut to avoid that the LP gets modified. that would mean the LP is not flushed * and the method SCIPgetLPBInvRow() fails; SCIP internally will apply that bound change automatically */ SCIP_CALL( SCIPaddCut(scip, NULL, cut, TRUE) ); naddedcuts++; } else { /* Only take efficacious cuts, except for cuts with one non-zero coefficients (= bound * changes); the latter cuts will be handeled internally in sepastore. */ if( SCIPisCutEfficacious(scip, NULL, cut) ) { assert(success == TRUE); SCIPdebugMessage(" -> gomory cut for <%s>: act=%f, rhs=%f, eff=%f\n", c >= 0 ? SCIPvarGetName(SCIPcolGetVar(cols[c])) : SCIProwGetName(rows[-c-1]), cutact, cutrhs, SCIPgetCutEfficacy(scip, NULL, cut)); if( sepadata->makeintegral ) { /* try to scale the cut to integral values */ SCIP_CALL( SCIPmakeRowIntegral(scip, cut, -SCIPepsilon(scip), SCIPsumepsilon(scip), maxdnom, maxscale, MAKECONTINTEGRAL, &success) ); if( sepadata->forcecuts ) success = TRUE; /* in case the left hand side in minus infinity and the right hand side is plus infinity the cut is * useless so we are not taking it at all */ if( (SCIPisInfinity(scip, -SCIProwGetLhs(cut)) && SCIPisInfinity(scip, SCIProwGetRhs(cut))) ) success = FALSE; /* @todo Trying to make the Gomory cut integral might fail. Due to numerical reasons/arguments we * currently ignore such cuts. If the cut, however, has small support (let's say smaller or equal to * 5), we might want to add that cut (even it does not have integral coefficients). To be able to * do that we need to add a rank to the data structure of a row. The rank of original rows are * zero and for aggregated rows it is the maximum over all used rows plus one. */ } if( success ) { SCIPdebugMessage(" -> found gomory cut <%s>: act=%f, rhs=%f, norm=%f, eff=%f, min=%f, max=%f (range=%f)\n", cutname, SCIPgetRowLPActivity(scip, cut), SCIProwGetRhs(cut), SCIProwGetNorm(cut), SCIPgetCutEfficacy(scip, NULL, cut), SCIPgetRowMinCoef(scip, cut), SCIPgetRowMaxCoef(scip, cut), SCIPgetRowMaxCoef(scip, cut)/SCIPgetRowMinCoef(scip, cut)); /* flush all changes before adding the cut */ SCIP_CALL( SCIPflushRowExtensions(scip, cut) ); /* add global cuts which are not implicit bound changes to the cut pool */ if( !cutislocal ) { if( sepadata->delayedcuts ) { SCIP_CALL( SCIPaddDelayedPoolCut(scip, cut) ); } else { SCIP_CALL( SCIPaddPoolCut(scip, cut) ); } } else { /* local cuts we add to the sepastore */ SCIP_CALL( SCIPaddCut(scip, NULL, cut, FALSE) ); } naddedcuts++; } } } /* release the row */ SCIP_CALL( SCIPreleaseRow(scip, &cut) ); } } } /* free temporary memory */ SCIPfreeBufferArray(scip, &binvrow); SCIPfreeBufferArray(scip, &basisind); SCIPfreeBufferArray(scip, &cutcoefs); SCIPdebugMessage("end searching gomory cuts: found %d cuts\n", naddedcuts); sepadata->lastncutsfound = SCIPgetNCutsFound(scip); /* evalute the result of the separation */ if( cutoff ) *result = SCIP_CUTOFF; else if ( naddedcuts > 0 ) *result = SCIP_SEPARATED; else *result = SCIP_DIDNOTFIND; return SCIP_OKAY; }
/** LP solution separation method for disjunctive cuts */ static SCIP_DECL_SEPAEXECLP(sepaExeclpDisjunctive) { SCIP_SEPADATA* sepadata; SCIP_CONSHDLR* conshdlr; SCIP_DIGRAPH* conflictgraph; SCIP_ROW** rows; SCIP_COL** cols; SCIP_Real* cutcoefs = NULL; SCIP_Real* simplexcoefs1 = NULL; SCIP_Real* simplexcoefs2 = NULL; SCIP_Real* coef = NULL; SCIP_Real* binvrow = NULL; SCIP_Real* rowsmaxval = NULL; SCIP_Real* violationarray = NULL; int* fixings1 = NULL; int* fixings2 = NULL; int* basisind = NULL; int* basisrow = NULL; int* varrank = NULL; int* edgearray = NULL; int nedges; int ndisjcuts; int nrelevantedges; int nsos1vars; int nconss; int maxcuts; int ncalls; int depth; int ncols; int nrows; int ind; int j; int i; assert( sepa != NULL ); assert( strcmp(SCIPsepaGetName(sepa), SEPA_NAME) == 0 ); assert( scip != NULL ); assert( result != NULL ); *result = SCIP_DIDNOTRUN; /* only generate disjunctive cuts if we are not close to terminating */ if ( SCIPisStopped(scip) ) return SCIP_OKAY; /* only generate disjunctive cuts if an optimal LP solution is at hand */ if ( SCIPgetLPSolstat(scip) != SCIP_LPSOLSTAT_OPTIMAL ) return SCIP_OKAY; /* only generate disjunctive cuts if the LP solution is basic */ if ( ! SCIPisLPSolBasic(scip) ) return SCIP_OKAY; /* get LP data */ SCIP_CALL( SCIPgetLPColsData(scip, &cols, &ncols) ); SCIP_CALL( SCIPgetLPRowsData(scip, &rows, &nrows) ); /* return if LP has no columns or no rows */ if ( ncols == 0 || nrows == 0 ) return SCIP_OKAY; assert( cols != NULL ); assert( rows != NULL ); /* get sepa data */ sepadata = SCIPsepaGetData(sepa); assert( sepadata != NULL ); /* get constraint handler */ conshdlr = sepadata->conshdlr; if ( conshdlr == NULL ) return SCIP_OKAY; /* get number of constraints */ nconss = SCIPconshdlrGetNConss(conshdlr); if ( nconss == 0 ) return SCIP_OKAY; /* check for maxdepth < depth, maxinvcutsroot = 0 and maxinvcuts = 0 */ depth = SCIPgetDepth(scip); if ( ( sepadata->maxdepth >= 0 && sepadata->maxdepth < depth ) || ( depth == 0 && sepadata->maxinvcutsroot == 0 ) || ( depth > 0 && sepadata->maxinvcuts == 0 ) ) return SCIP_OKAY; /* only call the cut separator a given number of times at each node */ ncalls = SCIPsepaGetNCallsAtNode(sepa); if ( (depth == 0 && sepadata->maxroundsroot >= 0 && ncalls >= sepadata->maxroundsroot) || (depth > 0 && sepadata->maxrounds >= 0 && ncalls >= sepadata->maxrounds) ) return SCIP_OKAY; /* get conflict graph and number of conflict graph edges (note that the digraph arcs were added in both directions) */ conflictgraph = SCIPgetConflictgraphSOS1(conshdlr); nedges = (int)SCIPceil(scip, (SCIP_Real)SCIPdigraphGetNArcs(conflictgraph)/2); /* if too many conflict graph edges, the separator can be slow: delay it until no other cuts have been found */ if ( sepadata->maxconfsdelay >= 0 && nedges >= sepadata->maxconfsdelay ) { int ncutsfound; ncutsfound = SCIPgetNCutsFound(scip); if ( ncutsfound > sepadata->lastncutsfound || ! SCIPsepaWasLPDelayed(sepa) ) { sepadata->lastncutsfound = ncutsfound; *result = SCIP_DELAYED; return SCIP_OKAY; } } /* check basis status */ for (j = 0; j < ncols; ++j) { if ( SCIPcolGetBasisStatus(cols[j]) == SCIP_BASESTAT_ZERO ) return SCIP_OKAY; } /* get number of SOS1 variables */ nsos1vars = SCIPgetNSOS1Vars(conshdlr); /* allocate buffer arrays */ SCIP_CALL( SCIPallocBufferArray(scip, &edgearray, nedges) ); SCIP_CALL( SCIPallocBufferArray(scip, &fixings1, nedges) ); SCIP_CALL( SCIPallocBufferArray(scip, &fixings2, nedges) ); SCIP_CALL( SCIPallocBufferArray(scip, &violationarray, nedges) ); /* get all violated conflicts {i, j} in the conflict graph and sort them based on the degree of a violation value */ nrelevantedges = 0; for (j = 0; j < nsos1vars; ++j) { SCIP_VAR* var; var = SCIPnodeGetVarSOS1(conflictgraph, j); if ( SCIPvarIsActive(var) && ! SCIPisFeasZero(scip, SCIPcolGetPrimsol(SCIPvarGetCol(var))) && SCIPcolGetBasisStatus(SCIPvarGetCol(var)) == SCIP_BASESTAT_BASIC ) { int* succ; int nsucc; /* get successors and number of successors */ nsucc = SCIPdigraphGetNSuccessors(conflictgraph, j); succ = SCIPdigraphGetSuccessors(conflictgraph, j); for (i = 0; i < nsucc; ++i) { SCIP_VAR* varsucc; int succind; succind = succ[i]; varsucc = SCIPnodeGetVarSOS1(conflictgraph, succind); if ( SCIPvarIsActive(varsucc) && succind < j && ! SCIPisFeasZero(scip, SCIPgetSolVal(scip, NULL, varsucc) ) && SCIPcolGetBasisStatus(SCIPvarGetCol(varsucc)) == SCIP_BASESTAT_BASIC ) { fixings1[nrelevantedges] = j; fixings2[nrelevantedges] = succind; edgearray[nrelevantedges] = nrelevantedges; violationarray[nrelevantedges++] = SCIPgetSolVal(scip, NULL, var) * SCIPgetSolVal(scip, NULL, varsucc); } } } } /* sort violation score values */ if ( nrelevantedges > 0) SCIPsortDownRealInt(violationarray, edgearray, nrelevantedges); else { SCIPfreeBufferArrayNull(scip, &violationarray); SCIPfreeBufferArrayNull(scip, &fixings2); SCIPfreeBufferArrayNull(scip, &fixings1); SCIPfreeBufferArrayNull(scip, &edgearray); return SCIP_OKAY; } SCIPfreeBufferArrayNull(scip, &violationarray); /* compute maximal number of cuts */ if ( SCIPgetDepth(scip) == 0 ) maxcuts = MIN(sepadata->maxinvcutsroot, nrelevantedges); else maxcuts = MIN(sepadata->maxinvcuts, nrelevantedges); assert( maxcuts > 0 ); /* allocate buffer arrays */ SCIP_CALL( SCIPallocBufferArray(scip, &varrank, ncols) ); SCIP_CALL( SCIPallocBufferArray(scip, &rowsmaxval, nrows) ); SCIP_CALL( SCIPallocBufferArray(scip, &basisrow, ncols) ); SCIP_CALL( SCIPallocBufferArray(scip, &binvrow, nrows) ); SCIP_CALL( SCIPallocBufferArray(scip, &coef, ncols) ); SCIP_CALL( SCIPallocBufferArray(scip, &simplexcoefs1, ncols) ); SCIP_CALL( SCIPallocBufferArray(scip, &simplexcoefs2, ncols) ); SCIP_CALL( SCIPallocBufferArray(scip, &cutcoefs, ncols) ); SCIP_CALL( SCIPallocBufferArray(scip, &basisind, nrows) ); /* get basis indices */ SCIP_CALL( SCIPgetLPBasisInd(scip, basisind) ); /* create vector "basisrow" with basisrow[column of non-slack basis variable] = corresponding row of B^-1; * compute maximum absolute value of nonbasic row coefficients */ for (j = 0; j < nrows; ++j) { SCIP_COL** rowcols; SCIP_Real* rowvals; SCIP_ROW* row; SCIP_Real val; SCIP_Real max = 0.0; int nnonz; /* fill basisrow vector */ ind = basisind[j]; if ( ind >= 0 ) basisrow[ind] = j; /* compute maximum absolute value of nonbasic row coefficients */ row = rows[j]; assert( row != NULL ); rowvals = SCIProwGetVals(row); nnonz = SCIProwGetNNonz(row); rowcols = SCIProwGetCols(row); for (i = 0; i < nnonz; ++i) { if ( SCIPcolGetBasisStatus(rowcols[i]) == SCIP_BASESTAT_LOWER || SCIPcolGetBasisStatus(rowcols[i]) == SCIP_BASESTAT_UPPER ) { val = REALABS(rowvals[i]); if ( SCIPisFeasGT(scip, val, max) ) max = REALABS(val); } } /* handle slack variable coefficient and save maximum value */ rowsmaxval[j] = MAX(max, 1.0); } /* initialize variable ranks with -1 */ for (j = 0; j < ncols; ++j) varrank[j] = -1; /* free buffer array */ SCIPfreeBufferArrayNull(scip, &basisind); /* for the most promising disjunctions: try to generate disjunctive cuts */ ndisjcuts = 0; for (i = 0; i < maxcuts; ++i) { SCIP_Bool madeintegral; SCIP_Real cutlhs1; SCIP_Real cutlhs2; SCIP_Real bound1; SCIP_Real bound2; SCIP_ROW* row = NULL; SCIP_VAR* var; SCIP_COL* col; int nonbasicnumber; int cutrank = 0; int edgenumber; int rownnonz; edgenumber = edgearray[i]; /* determine first simplex row */ var = SCIPnodeGetVarSOS1(conflictgraph, fixings1[edgenumber]); col = SCIPvarGetCol(var); ind = SCIPcolGetLPPos(col); assert( ind >= 0 ); assert( SCIPcolGetBasisStatus(col) == SCIP_BASESTAT_BASIC ); /* get the 'ind'th row of B^-1 and B^-1 \cdot A */ SCIP_CALL( SCIPgetLPBInvRow(scip, basisrow[ind], binvrow, NULL, NULL) ); SCIP_CALL( SCIPgetLPBInvARow(scip, basisrow[ind], binvrow, coef, NULL, NULL) ); /* get the simplex-coefficients of the non-basic variables */ SCIP_CALL( getSimplexCoefficients(scip, rows, nrows, cols, ncols, coef, binvrow, simplexcoefs1, &nonbasicnumber) ); /* get rank of variable if not known already */ if ( varrank[ind] < 0 ) varrank[ind] = getVarRank(scip, binvrow, rowsmaxval, sepadata->maxweightrange, rows, nrows); cutrank = MAX(cutrank, varrank[ind]); /* get right hand side and bound of simplex talbeau row */ cutlhs1 = SCIPcolGetPrimsol(col); if ( SCIPisFeasPositive(scip, cutlhs1) ) bound1 = SCIPcolGetUb(col); else bound1 = SCIPcolGetLb(col); /* determine second simplex row */ var = SCIPnodeGetVarSOS1(conflictgraph, fixings2[edgenumber]); col = SCIPvarGetCol(var); ind = SCIPcolGetLPPos(col); assert( ind >= 0 ); assert( SCIPcolGetBasisStatus(col) == SCIP_BASESTAT_BASIC ); /* get the 'ind'th row of B^-1 and B^-1 \cdot A */ SCIP_CALL( SCIPgetLPBInvRow(scip, basisrow[ind], binvrow, NULL, NULL) ); SCIP_CALL( SCIPgetLPBInvARow(scip, basisrow[ind], binvrow, coef, NULL, NULL) ); /* get the simplex-coefficients of the non-basic variables */ SCIP_CALL( getSimplexCoefficients(scip, rows, nrows, cols, ncols, coef, binvrow, simplexcoefs2, &nonbasicnumber) ); /* get rank of variable if not known already */ if ( varrank[ind] < 0 ) varrank[ind] = getVarRank(scip, binvrow, rowsmaxval, sepadata->maxweightrange, rows, nrows); cutrank = MAX(cutrank, varrank[ind]); /* get right hand side and bound of simplex talbeau row */ cutlhs2 = SCIPcolGetPrimsol(col); if ( SCIPisFeasPositive(scip, cutlhs2) ) bound2 = SCIPcolGetUb(col); else bound2 = SCIPcolGetLb(col); /* add coefficients to cut */ SCIP_CALL( generateDisjCutSOS1(scip, sepa, rows, nrows, cols, ncols, ndisjcuts, TRUE, sepadata->strengthen, cutlhs1, cutlhs2, bound1, bound2, simplexcoefs1, simplexcoefs2, cutcoefs, &row, &madeintegral) ); if ( row == NULL ) continue; /* raise cutrank for present cut */ ++cutrank; /* check if there are numerical evidences */ if ( ( madeintegral && ( sepadata->maxrankintegral == -1 || cutrank <= sepadata->maxrankintegral ) ) || ( ! madeintegral && ( sepadata->maxrank == -1 || cutrank <= sepadata->maxrank ) ) ) { /* possibly add cut to LP if it is useful; in case the lhs of the cut is minus infinity (due to scaling) the cut is useless */ rownnonz = SCIProwGetNNonz(row); if ( rownnonz > 0 && ! SCIPisInfinity(scip, -SCIProwGetLhs(row)) && ! SCIProwIsInLP(row) && SCIPisCutEfficacious(scip, NULL, row) ) { SCIP_Bool infeasible; /* set cut rank */ SCIProwChgRank(row, cutrank); /* add cut */ SCIP_CALL( SCIPaddCut(scip, NULL, row, FALSE, &infeasible) ); SCIPdebug( SCIP_CALL( SCIPprintRow(scip, row, NULL) ) ); if ( infeasible ) { *result = SCIP_CUTOFF; break; } ++ndisjcuts; } } /* release row */ SCIP_CALL( SCIPreleaseRow(scip, &row) ); } /* save total number of cuts found so far */ sepadata->lastncutsfound = SCIPgetNCutsFound(scip); /* evaluate the result of the separation */ if ( *result != SCIP_CUTOFF ) { if ( ndisjcuts > 0 ) *result = SCIP_SEPARATED; else *result = SCIP_DIDNOTFIND; } SCIPdebugMessage("Number of found disjunctive cuts: %d.\n", ndisjcuts); /* free buffer arrays */ SCIPfreeBufferArrayNull(scip, &cutcoefs); SCIPfreeBufferArrayNull(scip, &simplexcoefs2); SCIPfreeBufferArrayNull(scip, &simplexcoefs1); SCIPfreeBufferArrayNull(scip, &coef); SCIPfreeBufferArrayNull(scip, &binvrow); SCIPfreeBufferArrayNull(scip, &basisrow); SCIPfreeBufferArrayNull(scip, &fixings2); SCIPfreeBufferArrayNull(scip, &fixings1); SCIPfreeBufferArrayNull(scip, &edgearray); SCIPfreeBufferArrayNull(scip, &rowsmaxval); SCIPfreeBufferArrayNull(scip, &varrank); return SCIP_OKAY; }
/** LP solution separation method of separator */ static SCIP_DECL_SEPAEXECLP(sepaExeclpStrongcg) { /*lint --e{715}*/ SCIP_SEPADATA* sepadata; SCIP_VAR** vars; SCIP_COL** cols; SCIP_ROW** rows; SCIP_Real* varsolvals; SCIP_Real* binvrow; SCIP_Real* cutcoefs; SCIP_Real cutrhs; SCIP_Real cutact; SCIP_Real maxscale; SCIP_Longint maxdnom; int* basisind; int* inds; int ninds; int nvars; int ncols; int nrows; int ncalls; int depth; int maxdepth; int maxsepacuts; int ncuts; int c; int i; int cutrank; SCIP_Bool success; SCIP_Bool cutislocal; char normtype; assert(sepa != NULL); assert(strcmp(SCIPsepaGetName(sepa), SEPA_NAME) == 0); assert(scip != NULL); assert(result != NULL); *result = SCIP_DIDNOTRUN; sepadata = SCIPsepaGetData(sepa); assert(sepadata != NULL); depth = SCIPgetDepth(scip); ncalls = SCIPsepaGetNCallsAtNode(sepa); /* only call separator, if we are not close to terminating */ if( SCIPisStopped(scip) ) return SCIP_OKAY; /* only call the strong CG cut separator a given number of times at each node */ if( (depth == 0 && sepadata->maxroundsroot >= 0 && ncalls >= sepadata->maxroundsroot) || (depth > 0 && sepadata->maxrounds >= 0 && ncalls >= sepadata->maxrounds) ) return SCIP_OKAY; /* only call separator, if an optimal LP solution is at hand */ if( SCIPgetLPSolstat(scip) != SCIP_LPSOLSTAT_OPTIMAL ) return SCIP_OKAY; /* only call separator, if the LP solution is basic */ if( !SCIPisLPSolBasic(scip) ) return SCIP_OKAY; /* only call separator, if there are fractional variables */ if( SCIPgetNLPBranchCands(scip) == 0 ) return SCIP_OKAY; /* get variables data */ SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL) ); /* get LP data */ SCIP_CALL( SCIPgetLPColsData(scip, &cols, &ncols) ); SCIP_CALL( SCIPgetLPRowsData(scip, &rows, &nrows) ); if( ncols == 0 || nrows == 0 ) return SCIP_OKAY; #if 0 /* if too many columns, separator is usually very slow: delay it until no other cuts have been found */ if( ncols >= 50*nrows ) return SCIP_OKAY; if( ncols >= 5*nrows ) { int ncutsfound; ncutsfound = SCIPgetNCutsFound(scip); if( ncutsfound > sepadata->lastncutsfound || !SCIPsepaWasLPDelayed(sepa) ) { sepadata->lastncutsfound = ncutsfound; *result = SCIP_DELAYED; return SCIP_OKAY; } } #endif /* get the type of norm to use for efficacy calculations */ SCIP_CALL( SCIPgetCharParam(scip, "separating/efficacynorm", &normtype) ); /* set the maximal denominator in rational representation of strong CG cut and the maximal scale factor to * scale resulting cut to integral values to avoid numerical instabilities */ /**@todo find better but still stable strong CG cut settings: look at dcmulti, gesa3, khb0525, misc06, p2756 */ maxdepth = SCIPgetMaxDepth(scip); if( depth == 0 ) { maxdnom = 1000; maxscale = 1000.0; } else if( depth <= maxdepth/4 ) { maxdnom = 1000; maxscale = 1000.0; } else if( depth <= maxdepth/2 ) { maxdnom = 100; maxscale = 100.0; } else { maxdnom = 10; maxscale = 10.0; } *result = SCIP_DIDNOTFIND; /* allocate temporary memory */ SCIP_CALL( SCIPallocBufferArray(scip, &cutcoefs, nvars) ); SCIP_CALL( SCIPallocBufferArray(scip, &basisind, nrows) ); SCIP_CALL( SCIPallocBufferArray(scip, &binvrow, nrows) ); SCIP_CALL( SCIPallocBufferArray(scip, &inds, nrows) ); varsolvals = NULL; /* allocate this later, if needed */ /* get basis indices */ SCIP_CALL( SCIPgetLPBasisInd(scip, basisind) ); /* get the maximal number of cuts allowed in a separation round */ if( depth == 0 ) maxsepacuts = sepadata->maxsepacutsroot; else maxsepacuts = sepadata->maxsepacuts; SCIPdebugMessage("searching strong CG cuts: %d cols, %d rows, maxdnom=%" SCIP_LONGINT_FORMAT ", maxscale=%g, maxcuts=%d\n", ncols, nrows, maxdnom, maxscale, maxsepacuts); /* for all basic columns belonging to integer variables, try to generate a strong CG cut */ ncuts = 0; for( i = 0; i < nrows && ncuts < maxsepacuts && !SCIPisStopped(scip) && *result != SCIP_CUTOFF; ++i ) { SCIP_Bool tryrow; tryrow = FALSE; c = basisind[i]; if( c >= 0 ) { SCIP_VAR* var; assert(c < ncols); var = SCIPcolGetVar(cols[c]); if( SCIPvarGetType(var) != SCIP_VARTYPE_CONTINUOUS ) { SCIP_Real primsol; primsol = SCIPcolGetPrimsol(cols[c]); assert(SCIPgetVarSol(scip, var) == primsol); /*lint !e777*/ if( SCIPfeasFrac(scip, primsol) >= MINFRAC ) { SCIPdebugMessage("trying strong CG cut for col <%s> [%g]\n", SCIPvarGetName(var), primsol); tryrow = TRUE; } } } #ifdef SEPARATEROWS else { SCIP_ROW* row; assert(0 <= -c-1 && -c-1 < nrows); row = rows[-c-1]; if( SCIProwIsIntegral(row) && !SCIProwIsModifiable(row) ) { SCIP_Real primsol; primsol = SCIPgetRowActivity(scip, row); if( SCIPfeasFrac(scip, primsol) >= MINFRAC ) { SCIPdebugMessage("trying strong CG cut for row <%s> [%g]\n", SCIProwGetName(row), primsol); tryrow = TRUE; } } } #endif if( tryrow ) { /* get the row of B^-1 for this basic integer variable with fractional solution value */ SCIP_CALL( SCIPgetLPBInvRow(scip, i, binvrow, inds, &ninds) ); #ifdef SCIP_DEBUG /* initialize variables, that might not have been initialized in SCIPcalcMIR if success == FALSE */ cutact = 0.0; cutrhs = SCIPinfinity(scip); #endif /* create a strong CG cut out of the weighted LP rows using the B^-1 row as weights */ SCIP_CALL( SCIPcalcStrongCG(scip, BOUNDSWITCH, USEVBDS, ALLOWLOCAL, (int) MAXAGGRLEN(nvars), sepadata->maxweightrange, MINFRAC, MAXFRAC, binvrow, inds, ninds, 1.0, cutcoefs, &cutrhs, &cutact, &success, &cutislocal, &cutrank) ); assert(ALLOWLOCAL || !cutislocal); SCIPdebugMessage(" -> success=%u: %g <= %g\n", success, cutact, cutrhs); /* if successful, convert dense cut into sparse row, and add the row as a cut */ if( success && SCIPisFeasGT(scip, cutact, cutrhs) ) { SCIP_VAR** cutvars; SCIP_Real* cutvals; SCIP_Real cutnorm; int cutlen; /* if this is the first successful cut, get the LP solution for all COLUMN variables */ if( varsolvals == NULL ) { int v; SCIP_CALL( SCIPallocBufferArray(scip, &varsolvals, nvars) ); for( v = 0; v < nvars; ++v ) { if( SCIPvarGetStatus(vars[v]) == SCIP_VARSTATUS_COLUMN ) varsolvals[v] = SCIPvarGetLPSol(vars[v]); } } assert(varsolvals != NULL); /* get temporary memory for storing the cut as sparse row */ SCIP_CALL( SCIPallocBufferArray(scip, &cutvars, nvars) ); SCIP_CALL( SCIPallocBufferArray(scip, &cutvals, nvars) ); /* store the cut as sparse row, calculate activity and norm of cut */ SCIP_CALL( storeCutInArrays(scip, nvars, vars, cutcoefs, varsolvals, normtype, cutvars, cutvals, &cutlen, &cutact, &cutnorm) ); SCIPdebugMessage(" -> strong CG cut for <%s>: act=%f, rhs=%f, norm=%f, eff=%f, rank=%d\n", c >= 0 ? SCIPvarGetName(SCIPcolGetVar(cols[c])) : SCIProwGetName(rows[-c-1]), cutact, cutrhs, cutnorm, (cutact - cutrhs)/cutnorm, cutrank); if( SCIPisPositive(scip, cutnorm) && SCIPisEfficacious(scip, (cutact - cutrhs)/cutnorm) ) { SCIP_ROW* cut; char cutname[SCIP_MAXSTRLEN]; /* create the cut */ if( c >= 0 ) (void) SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "scg%d_x%d", SCIPgetNLPs(scip), c); else (void) SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "scg%d_s%d", SCIPgetNLPs(scip), -c-1); SCIP_CALL( SCIPcreateEmptyRowSepa(scip, &cut, sepa, cutname, -SCIPinfinity(scip), cutrhs, cutislocal, FALSE, sepadata->dynamiccuts) ); SCIP_CALL( SCIPaddVarsToRow(scip, cut, cutlen, cutvars, cutvals) ); /*SCIPdebug( SCIP_CALL(SCIPprintRow(scip, cut, NULL)) );*/ SCIProwChgRank(cut, cutrank); assert(success); #ifdef MAKECUTINTEGRAL /* try to scale the cut to integral values */ SCIP_CALL( SCIPmakeRowIntegral(scip, cut, -SCIPepsilon(scip), SCIPsumepsilon(scip), maxdnom, maxscale, MAKECONTINTEGRAL, &success) ); #else #ifdef MAKEINTCUTINTEGRAL /* try to scale the cut to integral values if there are no continuous variables * -> leads to an integral slack variable that can later be used for other cuts */ { int k = 0; while ( k < cutlen && SCIPvarIsIntegral(cutvars[k]) ) ++k; if( k == cutlen ) { SCIP_CALL( SCIPmakeRowIntegral(scip, cut, -SCIPepsilon(scip), SCIPsumepsilon(scip), maxdnom, maxscale, MAKECONTINTEGRAL, &success) ); } } #endif #endif #ifndef FORCECUTINTEGRAL success = TRUE; #endif if( success ) { if( !SCIPisCutEfficacious(scip, NULL, cut) ) { SCIPdebugMessage(" -> strong CG cut <%s> no longer efficacious: act=%f, rhs=%f, norm=%f, eff=%f\n", cutname, SCIPgetRowLPActivity(scip, cut), SCIProwGetRhs(cut), SCIProwGetNorm(cut), SCIPgetCutEfficacy(scip, NULL, cut)); /*SCIPdebug( SCIP_CALL(SCIPprintRow(scip, cut, NULL)) );*/ success = FALSE; } else { SCIP_Bool infeasible; SCIPdebugMessage(" -> found strong CG cut <%s>: act=%f, rhs=%f, norm=%f, eff=%f, min=%f, max=%f (range=%f)\n", cutname, SCIPgetRowLPActivity(scip, cut), SCIProwGetRhs(cut), SCIProwGetNorm(cut), SCIPgetCutEfficacy(scip, NULL, cut), SCIPgetRowMinCoef(scip, cut), SCIPgetRowMaxCoef(scip, cut), SCIPgetRowMaxCoef(scip, cut)/SCIPgetRowMinCoef(scip, cut)); /*SCIPdebug( SCIP_CALL(SCIPprintRow(scip, cut, NULL)) );*/ SCIP_CALL( SCIPaddCut(scip, NULL, cut, FALSE, &infeasible) ); if ( infeasible ) *result = SCIP_CUTOFF; else { if( !cutislocal ) { SCIP_CALL( SCIPaddPoolCut(scip, cut) ); } *result = SCIP_SEPARATED; } ncuts++; } } else { SCIPdebugMessage(" -> strong CG cut <%s> couldn't be scaled to integral coefficients: act=%f, rhs=%f, norm=%f, eff=%f\n", cutname, cutact, cutrhs, cutnorm, SCIPgetCutEfficacy(scip, NULL, cut)); } /* release the row */ SCIP_CALL( SCIPreleaseRow(scip, &cut) ); } /* free temporary memory */ SCIPfreeBufferArray(scip, &cutvals); SCIPfreeBufferArray(scip, &cutvars); } } } /* free temporary memory */ SCIPfreeBufferArrayNull(scip, &varsolvals); SCIPfreeBufferArray(scip, &inds); SCIPfreeBufferArray(scip, &binvrow); SCIPfreeBufferArray(scip, &basisind); SCIPfreeBufferArray(scip, &cutcoefs); SCIPdebugMessage("end searching strong CG cuts: found %d cuts\n", ncuts); sepadata->lastncutsfound = SCIPgetNCutsFound(scip); return SCIP_OKAY; }
/** execution method of primal heuristic */ static SCIP_DECL_HEUREXEC(heurExecShifting) /*lint --e{715}*/ { /*lint --e{715}*/ SCIP_HEURDATA* heurdata; SCIP_SOL* sol; SCIP_VAR** lpcands; SCIP_Real* lpcandssol; SCIP_ROW** lprows; SCIP_Real* activities; SCIP_ROW** violrows; SCIP_Real* nincreases; SCIP_Real* ndecreases; int* violrowpos; int* nfracsinrow; SCIP_Real increaseweight; SCIP_Real obj; SCIP_Real bestshiftval; SCIP_Real minobj; int nlpcands; int nlprows; int nvars; int nfrac; int nviolrows; int nprevviolrows; int minnviolrows; int nnonimprovingshifts; int c; int r; SCIP_Longint nlps; SCIP_Longint ncalls; SCIP_Longint nsolsfound; SCIP_Longint nnodes; assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0); assert(scip != NULL); assert(result != NULL); assert(SCIPhasCurrentNodeLP(scip)); *result = SCIP_DIDNOTRUN; /* only call heuristic, if an optimal LP solution is at hand */ if( SCIPgetLPSolstat(scip) != SCIP_LPSOLSTAT_OPTIMAL ) return SCIP_OKAY; /* only call heuristic, if the LP objective value is smaller than the cutoff bound */ if( SCIPisGE(scip, SCIPgetLPObjval(scip), SCIPgetCutoffbound(scip)) ) return SCIP_OKAY; /* get heuristic data */ heurdata = SCIPheurGetData(heur); assert(heurdata != NULL); /* don't call heuristic, if we have already processed the current LP solution */ nlps = SCIPgetNLPs(scip); if( nlps == heurdata->lastlp ) return SCIP_OKAY; heurdata->lastlp = nlps; /* don't call heuristic, if it was not successful enough in the past */ ncalls = SCIPheurGetNCalls(heur); nsolsfound = 10*SCIPheurGetNBestSolsFound(heur) + SCIPheurGetNSolsFound(heur); nnodes = SCIPgetNNodes(scip); if( nnodes % ((ncalls/100)/(nsolsfound+1)+1) != 0 ) return SCIP_OKAY; /* get fractional variables, that should be integral */ /* todo check if heuristic should include implicit integer variables for its calculations */ SCIP_CALL( SCIPgetLPBranchCands(scip, &lpcands, &lpcandssol, NULL, &nlpcands, NULL, NULL) ); nfrac = nlpcands; /* only call heuristic, if LP solution is fractional */ if( nfrac == 0 ) return SCIP_OKAY; *result = SCIP_DIDNOTFIND; /* get LP rows */ SCIP_CALL( SCIPgetLPRowsData(scip, &lprows, &nlprows) ); SCIPdebugMessage("executing shifting heuristic: %d LP rows, %d fractionals\n", nlprows, nfrac); /* get memory for activities, violated rows, and row violation positions */ nvars = SCIPgetNVars(scip); SCIP_CALL( SCIPallocBufferArray(scip, &activities, nlprows) ); SCIP_CALL( SCIPallocBufferArray(scip, &violrows, nlprows) ); SCIP_CALL( SCIPallocBufferArray(scip, &violrowpos, nlprows) ); SCIP_CALL( SCIPallocBufferArray(scip, &nfracsinrow, nlprows) ); SCIP_CALL( SCIPallocBufferArray(scip, &nincreases, nvars) ); SCIP_CALL( SCIPallocBufferArray(scip, &ndecreases, nvars) ); BMSclearMemoryArray(nfracsinrow, nlprows); BMSclearMemoryArray(nincreases, nvars); BMSclearMemoryArray(ndecreases, nvars); /* get the activities for all globally valid rows; * the rows should be feasible, but due to numerical inaccuracies in the LP solver, they can be violated */ nviolrows = 0; for( r = 0; r < nlprows; ++r ) { SCIP_ROW* row; row = lprows[r]; assert(SCIProwGetLPPos(row) == r); if( !SCIProwIsLocal(row) ) { activities[r] = SCIPgetRowActivity(scip, row); if( SCIPisFeasLT(scip, activities[r], SCIProwGetLhs(row)) || SCIPisFeasGT(scip, activities[r], SCIProwGetRhs(row)) ) { violrows[nviolrows] = row; violrowpos[r] = nviolrows; nviolrows++; } else violrowpos[r] = -1; } } /* calc the current number of fractional variables in rows */ for( c = 0; c < nlpcands; ++c ) addFracCounter(nfracsinrow, nlprows, lpcands[c], +1); /* get the working solution from heuristic's local data */ sol = heurdata->sol; assert(sol != NULL); /* copy the current LP solution to the working solution */ SCIP_CALL( SCIPlinkLPSol(scip, sol) ); /* calculate the minimal objective value possible after rounding fractional variables */ minobj = SCIPgetSolTransObj(scip, sol); assert(minobj < SCIPgetCutoffbound(scip)); for( c = 0; c < nlpcands; ++c ) { obj = SCIPvarGetObj(lpcands[c]); bestshiftval = obj > 0.0 ? SCIPfeasFloor(scip, lpcandssol[c]) : SCIPfeasCeil(scip, lpcandssol[c]); minobj += obj * (bestshiftval - lpcandssol[c]); } /* try to shift remaining variables in order to become/stay feasible */ nnonimprovingshifts = 0; minnviolrows = INT_MAX; increaseweight = 1.0; while( (nfrac > 0 || nviolrows > 0) && nnonimprovingshifts < MAXSHIFTINGS ) { SCIP_VAR* shiftvar; SCIP_Real oldsolval; SCIP_Real newsolval; SCIP_Bool oldsolvalisfrac; int probindex; SCIPdebugMessage("shifting heuristic: nfrac=%d, nviolrows=%d, obj=%g (best possible obj: %g), cutoff=%g\n", nfrac, nviolrows, SCIPgetSolOrigObj(scip, sol), SCIPretransformObj(scip, minobj), SCIPretransformObj(scip, SCIPgetCutoffbound(scip))); nprevviolrows = nviolrows; /* choose next variable to process: * - if a violated row exists, shift a variable decreasing the violation, that has least impact on other rows * - otherwise, shift a variable, that has strongest devastating impact on rows in opposite direction */ shiftvar = NULL; oldsolval = 0.0; newsolval = 0.0; if( nviolrows > 0 && (nfrac == 0 || nnonimprovingshifts < MAXSHIFTINGS-1) ) { SCIP_ROW* row; int rowidx; int rowpos; int direction; rowidx = -1; rowpos = -1; row = NULL; if( nfrac > 0 ) { for( rowidx = nviolrows-1; rowidx >= 0; --rowidx ) { row = violrows[rowidx]; rowpos = SCIProwGetLPPos(row); assert(violrowpos[rowpos] == rowidx); if( nfracsinrow[rowpos] > 0 ) break; } } if( rowidx == -1 ) { rowidx = SCIPgetRandomInt(0, nviolrows-1, &heurdata->randseed); row = violrows[rowidx]; rowpos = SCIProwGetLPPos(row); assert(0 <= rowpos && rowpos < nlprows); assert(violrowpos[rowpos] == rowidx); assert(nfracsinrow[rowpos] == 0); } assert(violrowpos[rowpos] == rowidx); SCIPdebugMessage("shifting heuristic: try to fix violated row <%s>: %g <= %g <= %g\n", SCIProwGetName(row), SCIProwGetLhs(row), activities[rowpos], SCIProwGetRhs(row)); SCIPdebug( SCIP_CALL( SCIPprintRow(scip, row, NULL) ) ); /* get direction in which activity must be shifted */ assert(SCIPisFeasLT(scip, activities[rowpos], SCIProwGetLhs(row)) || SCIPisFeasGT(scip, activities[rowpos], SCIProwGetRhs(row))); direction = SCIPisFeasLT(scip, activities[rowpos], SCIProwGetLhs(row)) ? +1 : -1; /* search a variable that can shift the activity in the necessary direction */ SCIP_CALL( selectShifting(scip, sol, row, activities[rowpos], direction, nincreases, ndecreases, increaseweight, &shiftvar, &oldsolval, &newsolval) ); } if( shiftvar == NULL && nfrac > 0 ) { SCIPdebugMessage("shifting heuristic: search rounding variable and try to stay feasible\n"); SCIP_CALL( selectEssentialRounding(scip, sol, minobj, lpcands, nlpcands, &shiftvar, &oldsolval, &newsolval) ); } /* check, whether shifting was possible */ if( shiftvar == NULL || SCIPisEQ(scip, oldsolval, newsolval) ) { SCIPdebugMessage("shifting heuristic: -> didn't find a shifting variable\n"); break; } SCIPdebugMessage("shifting heuristic: -> shift var <%s>[%g,%g], type=%d, oldval=%g, newval=%g, obj=%g\n", SCIPvarGetName(shiftvar), SCIPvarGetLbGlobal(shiftvar), SCIPvarGetUbGlobal(shiftvar), SCIPvarGetType(shiftvar), oldsolval, newsolval, SCIPvarGetObj(shiftvar)); /* update row activities of globally valid rows */ SCIP_CALL( updateActivities(scip, activities, violrows, violrowpos, &nviolrows, nlprows, shiftvar, oldsolval, newsolval) ); if( nviolrows >= nprevviolrows ) nnonimprovingshifts++; else if( nviolrows < minnviolrows ) { minnviolrows = nviolrows; nnonimprovingshifts = 0; } /* store new solution value and decrease fractionality counter */ SCIP_CALL( SCIPsetSolVal(scip, sol, shiftvar, newsolval) ); /* update fractionality counter and minimal objective value possible after shifting remaining variables */ oldsolvalisfrac = !SCIPisFeasIntegral(scip, oldsolval) && (SCIPvarGetType(shiftvar) == SCIP_VARTYPE_BINARY || SCIPvarGetType(shiftvar) == SCIP_VARTYPE_INTEGER); obj = SCIPvarGetObj(shiftvar); if( (SCIPvarGetType(shiftvar) == SCIP_VARTYPE_BINARY || SCIPvarGetType(shiftvar) == SCIP_VARTYPE_INTEGER) && oldsolvalisfrac ) { assert(SCIPisFeasIntegral(scip, newsolval)); nfrac--; nnonimprovingshifts = 0; minnviolrows = INT_MAX; addFracCounter(nfracsinrow, nlprows, shiftvar, -1); /* the rounding was already calculated into the minobj -> update only if rounding in "wrong" direction */ if( obj > 0.0 && newsolval > oldsolval ) minobj += obj; else if( obj < 0.0 && newsolval < oldsolval ) minobj -= obj; } else { /* update minimal possible objective value */ minobj += obj * (newsolval - oldsolval); } /* update increase/decrease arrays */ if( !oldsolvalisfrac ) { probindex = SCIPvarGetProbindex(shiftvar); assert(0 <= probindex && probindex < nvars); increaseweight *= WEIGHTFACTOR; if( newsolval < oldsolval ) ndecreases[probindex] += increaseweight; else nincreases[probindex] += increaseweight; if( increaseweight >= 1e+09 ) { int i; for( i = 0; i < nvars; ++i ) { nincreases[i] /= increaseweight; ndecreases[i] /= increaseweight; } increaseweight = 1.0; } } SCIPdebugMessage("shifting heuristic: -> nfrac=%d, nviolrows=%d, obj=%g (best possible obj: %g)\n", nfrac, nviolrows, SCIPgetSolOrigObj(scip, sol), SCIPretransformObj(scip, minobj)); } /* check, if the new solution is feasible */ if( nfrac == 0 && nviolrows == 0 ) { SCIP_Bool stored; /* check solution for feasibility, and add it to solution store if possible * neither integrality nor feasibility of LP rows has to be checked, because this is already * done in the shifting heuristic itself; however, we better check feasibility of LP rows, * because of numerical problems with activity updating */ SCIP_CALL( SCIPtrySol(scip, sol, FALSE, FALSE, FALSE, TRUE, &stored) ); if( stored ) { SCIPdebugMessage("found feasible shifted solution:\n"); SCIPdebug( SCIP_CALL( SCIPprintSol(scip, sol, NULL, FALSE) ) ); *result = SCIP_FOUNDSOL; } } /* free memory buffers */ SCIPfreeBufferArray(scip, &ndecreases); SCIPfreeBufferArray(scip, &nincreases); SCIPfreeBufferArray(scip, &nfracsinrow); SCIPfreeBufferArray(scip, &violrowpos); SCIPfreeBufferArray(scip, &violrows); SCIPfreeBufferArray(scip, &activities); return SCIP_OKAY; }
/** execution method of primal heuristic */ static SCIP_DECL_HEUREXEC(heurExecRounding) /*lint --e{715}*/ { /*lint --e{715}*/ SCIP_HEURDATA* heurdata; SCIP_SOL* sol; SCIP_VAR** lpcands; SCIP_Real* lpcandssol; SCIP_ROW** lprows; SCIP_Real* activities; SCIP_ROW** violrows; int* violrowpos; SCIP_Real obj; SCIP_Real bestroundval; SCIP_Real minobj; int nlpcands; int nlprows; int nfrac; int nviolrows; int c; int r; SCIP_Longint nlps; SCIP_Longint ncalls; SCIP_Longint nsolsfound; SCIP_Longint nnodes; assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0); assert(scip != NULL); assert(result != NULL); assert(SCIPhasCurrentNodeLP(scip)); *result = SCIP_DIDNOTRUN; /* only call heuristic, if an optimal LP solution is at hand */ if( SCIPgetLPSolstat(scip) != SCIP_LPSOLSTAT_OPTIMAL ) return SCIP_OKAY; /* only call heuristic, if the LP objective value is smaller than the cutoff bound */ if( SCIPisGE(scip, SCIPgetLPObjval(scip), SCIPgetCutoffbound(scip)) ) return SCIP_OKAY; /* get heuristic data */ heurdata = SCIPheurGetData(heur); assert(heurdata != NULL); /* don't call heuristic, if we have already processed the current LP solution */ nlps = SCIPgetNLPs(scip); if( nlps == heurdata->lastlp ) return SCIP_OKAY; heurdata->lastlp = nlps; /* don't call heuristic, if it was not successful enough in the past */ ncalls = SCIPheurGetNCalls(heur); nsolsfound = 10*SCIPheurGetNBestSolsFound(heur) + SCIPheurGetNSolsFound(heur); nnodes = SCIPgetNNodes(scip); if( nnodes % ((ncalls/heurdata->successfactor)/(nsolsfound+1)+1) != 0 ) return SCIP_OKAY; /* get fractional variables, that should be integral */ SCIP_CALL( SCIPgetLPBranchCands(scip, &lpcands, &lpcandssol, NULL, &nlpcands, NULL, NULL) ); nfrac = nlpcands; /* only call heuristic, if LP solution is fractional */ if( nfrac == 0 ) return SCIP_OKAY; *result = SCIP_DIDNOTFIND; /* get LP rows */ SCIP_CALL( SCIPgetLPRowsData(scip, &lprows, &nlprows) ); SCIPdebugMessage("executing rounding heuristic: %d LP rows, %d fractionals\n", nlprows, nfrac); /* get memory for activities, violated rows, and row violation positions */ SCIP_CALL( SCIPallocBufferArray(scip, &activities, nlprows) ); SCIP_CALL( SCIPallocBufferArray(scip, &violrows, nlprows) ); SCIP_CALL( SCIPallocBufferArray(scip, &violrowpos, nlprows) ); /* get the activities for all globally valid rows; * the rows should be feasible, but due to numerical inaccuracies in the LP solver, they can be violated */ nviolrows = 0; for( r = 0; r < nlprows; ++r ) { SCIP_ROW* row; row = lprows[r]; assert(SCIProwGetLPPos(row) == r); if( !SCIProwIsLocal(row) ) { activities[r] = SCIPgetRowActivity(scip, row); if( SCIPisFeasLT(scip, activities[r], SCIProwGetLhs(row)) || SCIPisFeasGT(scip, activities[r], SCIProwGetRhs(row)) ) { violrows[nviolrows] = row; violrowpos[r] = nviolrows; nviolrows++; } else violrowpos[r] = -1; } } /* get the working solution from heuristic's local data */ sol = heurdata->sol; assert(sol != NULL); /* copy the current LP solution to the working solution */ SCIP_CALL( SCIPlinkLPSol(scip, sol) ); /* calculate the minimal objective value possible after rounding fractional variables */ minobj = SCIPgetSolTransObj(scip, sol); assert(minobj < SCIPgetCutoffbound(scip)); for( c = 0; c < nlpcands; ++c ) { obj = SCIPvarGetObj(lpcands[c]); bestroundval = obj > 0.0 ? SCIPfeasFloor(scip, lpcandssol[c]) : SCIPfeasCeil(scip, lpcandssol[c]); minobj += obj * (bestroundval - lpcandssol[c]); } /* try to round remaining variables in order to become/stay feasible */ while( nfrac > 0 ) { SCIP_VAR* roundvar; SCIP_Real oldsolval; SCIP_Real newsolval; SCIPdebugMessage("rounding heuristic: nfrac=%d, nviolrows=%d, obj=%g (best possible obj: %g)\n", nfrac, nviolrows, SCIPgetSolOrigObj(scip, sol), SCIPretransformObj(scip, minobj)); /* minobj < SCIPgetCutoffbound(scip) should be true, otherwise the rounding variable selection * should have returned NULL. Due to possible cancellation we use SCIPisLE. */ assert( SCIPisLE(scip, minobj, SCIPgetCutoffbound(scip)) ); /* choose next variable to process: * - if a violated row exists, round a variable decreasing the violation, that has least impact on other rows * - otherwise, round a variable, that has strongest devastating impact on rows in opposite direction */ if( nviolrows > 0 ) { SCIP_ROW* row; int rowpos; row = violrows[nviolrows-1]; rowpos = SCIProwGetLPPos(row); assert(0 <= rowpos && rowpos < nlprows); assert(violrowpos[rowpos] == nviolrows-1); SCIPdebugMessage("rounding heuristic: try to fix violated row <%s>: %g <= %g <= %g\n", SCIProwGetName(row), SCIProwGetLhs(row), activities[rowpos], SCIProwGetRhs(row)); if( SCIPisFeasLT(scip, activities[rowpos], SCIProwGetLhs(row)) ) { /* lhs is violated: select a variable rounding, that increases the activity */ SCIP_CALL( selectIncreaseRounding(scip, sol, minobj, row, &roundvar, &oldsolval, &newsolval) ); } else { assert(SCIPisFeasGT(scip, activities[rowpos], SCIProwGetRhs(row))); /* rhs is violated: select a variable rounding, that decreases the activity */ SCIP_CALL( selectDecreaseRounding(scip, sol, minobj, row, &roundvar, &oldsolval, &newsolval) ); } } else { SCIPdebugMessage("rounding heuristic: search rounding variable and try to stay feasible\n"); SCIP_CALL( selectEssentialRounding(scip, sol, minobj, lpcands, nlpcands, &roundvar, &oldsolval, &newsolval) ); } /* check, whether rounding was possible */ if( roundvar == NULL ) { SCIPdebugMessage("rounding heuristic: -> didn't find a rounding variable\n"); break; } SCIPdebugMessage("rounding heuristic: -> round var <%s>, oldval=%g, newval=%g, obj=%g\n", SCIPvarGetName(roundvar), oldsolval, newsolval, SCIPvarGetObj(roundvar)); /* update row activities of globally valid rows */ SCIP_CALL( updateActivities(scip, activities, violrows, violrowpos, &nviolrows, nlprows, roundvar, oldsolval, newsolval) ); /* store new solution value and decrease fractionality counter */ SCIP_CALL( SCIPsetSolVal(scip, sol, roundvar, newsolval) ); nfrac--; /* update minimal objective value possible after rounding remaining variables */ obj = SCIPvarGetObj(roundvar); if( obj > 0.0 && newsolval > oldsolval ) minobj += obj; else if( obj < 0.0 && newsolval < oldsolval ) minobj -= obj; SCIPdebugMessage("rounding heuristic: -> nfrac=%d, nviolrows=%d, obj=%g (best possible obj: %g)\n", nfrac, nviolrows, SCIPgetSolOrigObj(scip, sol), SCIPretransformObj(scip, minobj)); } /* check, if the new solution is feasible */ if( nfrac == 0 && nviolrows == 0 ) { SCIP_Bool stored; /* check solution for feasibility, and add it to solution store if possible * neither integrality nor feasibility of LP rows has to be checked, because this is already * done in the rounding heuristic itself; however, be better check feasibility of LP rows, * because of numerical problems with activity updating */ SCIP_CALL( SCIPtrySol(scip, sol, FALSE, FALSE, FALSE, TRUE, &stored) ); if( stored ) { #ifdef SCIP_DEBUG SCIPdebugMessage("found feasible rounded solution:\n"); SCIP_CALL( SCIPprintSol(scip, sol, NULL, FALSE) ); #endif *result = SCIP_FOUNDSOL; } } /* free memory buffers */ SCIPfreeBufferArray(scip, &violrowpos); SCIPfreeBufferArray(scip, &violrows); SCIPfreeBufferArray(scip, &activities); return SCIP_OKAY; }
/** execution method of primal heuristic */ static SCIP_DECL_HEUREXEC(heurExecOctane) { /*lint --e{715}*/ SCIP_HEURDATA* heurdata; SCIP_SOL* sol; SCIP_SOL** first_sols; /* stores the first ffirst sols in order to check for common violation of a row */ SCIP_VAR** vars; /* the variables of the problem */ SCIP_VAR** fracvars; /* variables, that are fractional in current LP solution */ SCIP_VAR** subspacevars; /* the variables on which the search is performed. Either coinciding with vars or with the * space of all fractional variables of the current LP solution */ SCIP_Real p; /* n/2 - <delta,x> ( for some facet delta ) */ SCIP_Real q; /* <delta,a> */ SCIP_Real* rayorigin; /* origin of the ray, vector x in paper */ SCIP_Real* raydirection; /* direction of the ray, vector a in paper */ SCIP_Real* negquotient; /* negated quotient of rayorigin and raydirection, vector v in paper */ SCIP_Real* lambda; /* stores the distance of the facets (s.b.) to the origin of the ray */ SCIP_Bool usefracspace; /* determines whether the search concentrates on fractional variables and fixes integer ones */ SCIP_Bool cons_viol; /* used for checking whether a linear constraint is violated by one of the possible solutions */ SCIP_Bool success; SCIP_Bool* sign; /* signature of the direction of the ray */ SCIP_Bool** facets; /* list of extended facets */ int nvars; /* number of variables */ int nbinvars; /* number of 0-1-variables */ int nfracvars; /* number of fractional variables in current LP solution */ int nsubspacevars; /* dimension of the subspace on which the search is performed */ int nfacets; /* number of facets hidden by the ray that where already found */ int i; /* counter */ int j; /* counter */ int f_max; /* {0,1}-points to be checked */ int f_first; /* {0,1}-points to be generated at first in order to check whether a restart is necessary */ int r; /* counter */ int firstrule; int* perm; /* stores the way in which the coordinates were permuted */ int* fracspace; /* maps the variables of the subspace to the original variables */ assert(heur != NULL); assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0); assert(scip != NULL); assert(result != NULL); assert(SCIPhasCurrentNodeLP(scip)); *result = SCIP_DELAYED; /* only call heuristic, if an optimal LP solution is at hand */ if( SCIPgetLPSolstat(scip) != SCIP_LPSOLSTAT_OPTIMAL ) return SCIP_OKAY; *result = SCIP_DIDNOTRUN; SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, &nbinvars, NULL, NULL, NULL) ); /* OCTANE is for use in 0-1 programs only */ if( nvars != nbinvars ) return SCIP_OKAY; /* get heuristic's data */ heurdata = SCIPheurGetData(heur); assert( heurdata != NULL ); /* don't call heuristic, if it was not successful enough in the past */ /*lint --e{647}*/ if( SCIPgetNNodes(scip) % (SCIPheurGetNCalls(heur) / (100 * SCIPheurGetNBestSolsFound(heur) + 10*heurdata->nsuccess + 1) + 1) != 0 ) return SCIP_OKAY; SCIP_CALL( SCIPgetLPBranchCands(scip, &fracvars, NULL, NULL, &nfracvars, NULL) ); /* don't use integral starting points */ if( nfracvars == 0 ) return SCIP_OKAY; /* get working pointers from heurdata */ sol = heurdata->sol; assert( sol != NULL ); f_max = heurdata->f_max; f_first = heurdata->f_first; usefracspace = heurdata->usefracspace; SCIP_CALL( SCIPallocBufferArray(scip, &fracspace, nvars) ); /* determine the space one which OCTANE should work either as the whole space or as the space of fractional variables */ if( usefracspace ) { nsubspacevars = nfracvars; SCIP_CALL( SCIPallocBufferArray(scip, &subspacevars, nsubspacevars) ); BMScopyMemoryArray(subspacevars, fracvars, nsubspacevars); for( i = nvars - 1; i >= 0; --i ) fracspace[i] = -1; for( i = nsubspacevars - 1; i >= 0; --i ) fracspace[SCIPvarGetProbindex(subspacevars[i])] = i; } else { int currentindex; nsubspacevars = nvars; SCIP_CALL( SCIPallocBufferArray(scip, &subspacevars, nsubspacevars) ); /* only copy the variables which are in the current LP */ currentindex = 0; for( i = 0; i < nvars; ++i ) { if( SCIPcolGetLPPos(SCIPvarGetCol(vars[i])) >= 0 ) { subspacevars[currentindex] = vars[i]; fracspace[i] = currentindex; ++currentindex; } else { fracspace[i] = -1; --nsubspacevars; } } } /* nothing to do for empty search space */ if( nsubspacevars == 0 ) return SCIP_OKAY; assert(0 < nsubspacevars && nsubspacevars <= nvars); for( i = 0; i < nsubspacevars; i++) assert(fracspace[SCIPvarGetProbindex(subspacevars[i])] == i); /* at most 2^(n-1) facets can be hit */ if( nsubspacevars < 30 ) { /*lint --e{701}*/ assert(f_max > 0); f_max = MIN(f_max, 1 << (nsubspacevars - 1) ); } f_first = MIN(f_first, f_max); /* memory allocation */ SCIP_CALL( SCIPallocBufferArray(scip, &rayorigin, nsubspacevars) ); SCIP_CALL( SCIPallocBufferArray(scip, &raydirection, nsubspacevars) ); SCIP_CALL( SCIPallocBufferArray(scip, &negquotient, nsubspacevars) ); SCIP_CALL( SCIPallocBufferArray(scip, &sign, nsubspacevars) ); SCIP_CALL( SCIPallocBufferArray(scip, &perm, nsubspacevars) ); SCIP_CALL( SCIPallocBufferArray(scip, &lambda, f_max + 1) ); SCIP_CALL( SCIPallocBufferArray(scip, &facets, f_max + 1) ); for( i = f_max; i >= 0; --i ) { /*lint --e{866}*/ SCIP_CALL( SCIPallocBufferArray(scip, &facets[i], nsubspacevars) ); } SCIP_CALL( SCIPallocBufferArray(scip, &first_sols, f_first) ); *result = SCIP_DIDNOTFIND; /* starting OCTANE */ SCIPdebugMessage("run Octane heuristic on %s variables, which are %d vars, generate at most %d facets, using rule number %d\n", usefracspace ? "fractional" : "all", nsubspacevars, f_max, (heurdata->lastrule+1)%5); /* generate starting point in original coordinates */ SCIP_CALL( generateStartingPoint(scip, rayorigin, subspacevars, nsubspacevars) ); for( i = nsubspacevars - 1; i >= 0; --i ) rayorigin[i] -= 0.5; firstrule = heurdata->lastrule; ++firstrule; for( r = firstrule; r <= firstrule + 10 && !SCIPisStopped(scip); r++ ) { SCIP_ROW** rows; int nrows; /* generate shooting ray in original coordinates by certain rules */ switch(r % 5) { case 1: if( heurdata->useavgnbray ) { SCIP_CALL( generateAverageNBRay(scip, raydirection, fracspace, subspacevars, nsubspacevars) ); } break; case 2: if( heurdata->useobjray ) { SCIP_CALL( generateObjectiveRay(scip, raydirection, subspacevars, nsubspacevars) ); } break; case 3: if( heurdata->usediffray ) { SCIP_CALL( generateDifferenceRay(scip, raydirection, subspacevars, nsubspacevars) ); } break; case 4: if( heurdata->useavgwgtray && SCIPisLPSolBasic(scip) ) { SCIP_CALL( generateAverageRay(scip, raydirection, subspacevars, nsubspacevars, TRUE) ); } break; case 0: if( heurdata->useavgray && SCIPisLPSolBasic(scip) ) { SCIP_CALL( generateAverageRay(scip, raydirection, subspacevars, nsubspacevars, FALSE) ); } break; default: SCIPerrorMessage("invalid ray rule identifier\n"); SCIPABORT(); } /* there must be a feasible direction for the shooting ray */ if( isZero(scip, raydirection, nsubspacevars) ) continue; /* transform coordinates such that raydirection >= 0 */ flipCoords(rayorigin, raydirection, sign, nsubspacevars); for( i = f_max - 1; i >= 0; --i) lambda[i] = SCIPinfinity(scip); /* calculate negquotient, initialize perm, facets[0], p, and q */ p = 0.5 * nsubspacevars; q = 0.0; for( i = nsubspacevars - 1; i >= 0; --i ) { /* calculate negquotient, the ratio of rayorigin and raydirection, paying special attention to the case raydirection[i] == 0 */ if( SCIPisFeasZero(scip, raydirection[i]) ) { if( rayorigin[i] < 0 ) negquotient[i] = SCIPinfinity(scip); else negquotient[i] = -SCIPinfinity(scip); } else negquotient[i] = - (rayorigin[i] / raydirection[i]); perm[i] = i; /* initialization of facets[0] to the all-one facet with p and q its characteristic values */ facets[0][i] = TRUE; p -= rayorigin[i]; q += raydirection[i]; } assert(SCIPisPositive(scip, q)); /* resort the coordinates in nonincreasing order of negquotient */ SCIPsortDownRealRealRealBoolPtr( negquotient, raydirection, rayorigin, sign, (void**) subspacevars, nsubspacevars); #ifndef NDEBUG for( i = 0; i < nsubspacevars; i++ ) assert( raydirection[i] >= 0 ); for( i = 1; i < nsubspacevars; i++ ) assert( negquotient[i - 1] >= negquotient[i] ); #endif /* finished initialization */ /* find the first facet of the octahedron hit by a ray shot from rayorigin into direction raydirection */ for( i = 0; i < nsubspacevars && negquotient[i] * q > p; ++i ) { facets[0][i] = FALSE; p += 2 * rayorigin[i]; q -= 2 * raydirection[i]; assert(SCIPisPositive(scip, p)); assert(SCIPisPositive(scip, q)); } /* avoid dividing by values close to 0.0 */ if( !SCIPisFeasPositive(scip, q) ) continue; /* assert necessary for flexelint */ assert(q > 0); lambda[0] = p / q; nfacets = 1; /* find the first facets hit by the ray */ for( i = 0; i < nfacets && i < f_first; ++i) generateNeighborFacets(scip, facets, lambda, rayorigin, raydirection, negquotient, nsubspacevars, f_max, i, &nfacets); /* construct the first ffirst possible solutions */ for( i = 0; i < nfacets && i < f_first; ++i ) { SCIP_CALL( SCIPcreateSol(scip, &first_sols[i], heur) ); SCIP_CALL( getSolFromFacet(scip, facets[i], first_sols[i], sign, subspacevars, nsubspacevars) ); assert( first_sols[i] != NULL ); } /* try, whether there is a row violated by all of the first ffirst solutions */ cons_viol = FALSE; SCIP_CALL( SCIPgetLPRowsData(scip, &rows, &nrows) ); for( i = nrows - 1; i >= 0; --i ) { if( !SCIProwIsLocal(rows[i]) ) { SCIP_COL** cols; SCIP_Real constant; SCIP_Real lhs; SCIP_Real rhs; SCIP_Real rowval; SCIP_Real* coeffs; int nnonzerovars; int k; /* get the row's data */ constant = SCIProwGetConstant(rows[i]); lhs = SCIProwGetLhs(rows[i]); rhs = SCIProwGetRhs(rows[i]); coeffs = SCIProwGetVals(rows[i]); nnonzerovars = SCIProwGetNNonz(rows[i]); cols = SCIProwGetCols(rows[i]); rowval = constant; for( j = nnonzerovars - 1; j >= 0; --j ) rowval += coeffs[j] * SCIPgetSolVal(scip, first_sols[0], SCIPcolGetVar(cols[j])); /* if the row's lhs is violated by the first sol, test, whether it is violated by the next ones, too */ if( lhs > rowval ) { cons_viol = TRUE; for( k = MIN(f_first, nfacets) - 1; k > 0; --k ) { rowval = constant; for( j = nnonzerovars - 1; j >= 0; --j ) rowval += coeffs[j] * SCIPgetSolVal(scip, first_sols[k], SCIPcolGetVar(cols[j])); if( lhs <= rowval ) { cons_viol = FALSE; break; } } } /* dito for the right hand side */ else if( rhs < rowval ) { cons_viol = TRUE; for( k = MIN(f_first, nfacets) - 1; k > 0; --k ) { rowval = constant; for( j = nnonzerovars - 1; j >= 0; --j ) rowval += coeffs[j] * SCIPgetSolVal(scip, first_sols[k], SCIPcolGetVar(cols[j])); if( rhs >= rowval ) { cons_viol = FALSE; break; } } } /* break as soon as one row is violated by all of the ffirst solutions */ if( cons_viol ) break; } } if( !cons_viol ) { /* if there was no row violated by all solutions, try whether one or more of them are feasible */ for( i = MIN(f_first, nfacets) - 1; i >= 0; --i ) { assert(first_sols[i] != NULL); SCIP_CALL( SCIPtrySol(scip, first_sols[i], FALSE, TRUE, FALSE, TRUE, &success) ); if( success ) *result = SCIP_FOUNDSOL; } /* search for further facets and construct and try solutions out of facets fixed as closest ones */ for( i = f_first; i < f_max; ++i) { if( i >= nfacets ) break; generateNeighborFacets(scip, facets, lambda, rayorigin, raydirection, negquotient, nsubspacevars, f_max, i, &nfacets); SCIP_CALL( getSolFromFacet(scip, facets[i], sol, sign, subspacevars, nsubspacevars) ); SCIP_CALL( SCIPtrySol(scip, sol, FALSE, TRUE, FALSE, TRUE, &success) ); if( success ) *result = SCIP_FOUNDSOL; } } /* finished OCTANE */ for( i = MIN(f_first, nfacets) - 1; i >= 0; --i ) { SCIP_CALL( SCIPfreeSol(scip, &first_sols[i]) ); } } heurdata->lastrule = r; if( *result == SCIP_FOUNDSOL ) ++(heurdata->nsuccess); /* free temporary memory */ SCIPfreeBufferArray(scip, &first_sols); for( i = f_max; i >= 0; --i ) SCIPfreeBufferArray(scip, &facets[i]); SCIPfreeBufferArray(scip, &facets); SCIPfreeBufferArray(scip, &lambda); SCIPfreeBufferArray(scip, &perm); SCIPfreeBufferArray(scip, &sign); SCIPfreeBufferArray(scip, &negquotient); SCIPfreeBufferArray(scip, &raydirection); SCIPfreeBufferArray(scip, &rayorigin); SCIPfreeBufferArray(scip, &subspacevars); SCIPfreeBufferArray(scip, &fracspace); return SCIP_OKAY; }
/** generates the direction of the shooting ray as the average of the normalized non-basic vars and rows */ static SCIP_RETCODE generateAverageNBRay( SCIP* scip, /**< SCIP data structure */ SCIP_Real* raydirection, /**< shooting ray */ int* fracspace, /**< index set of fractional variables */ SCIP_VAR** subspacevars, /**< pointer to fractional space variables */ int nsubspacevars /**< dimension of fractional space */ ) { SCIP_ROW** rows; SCIP_COL** cols; int nrows; int ncols; int i; assert(scip != NULL); assert(raydirection != NULL); assert(fracspace != NULL); assert(subspacevars != NULL); SCIP_CALL( SCIPgetLPRowsData(scip, &rows, &nrows) ); SCIP_CALL( SCIPgetLPColsData(scip, &cols, &ncols) ); /* add up non-basic variables */ for( i = nsubspacevars - 1; i >= 0; --i ) { SCIP_Real solval; solval = SCIPvarGetLPSol(subspacevars[i]); if( SCIPisFeasEQ(scip, solval, SCIPvarGetLbLocal(subspacevars[i])) ) raydirection[i] = +1.0; else if( SCIPisFeasEQ(scip, solval, SCIPvarGetUbLocal(subspacevars[i])) ) raydirection[i] = -1.0; else raydirection[i] = 0.0; } /* add up non-basic rows */ for( i = nrows - 1; i >= 0; --i ) { SCIP_Real dualsol; SCIP_Real factor; SCIP_Real* coeffs; SCIP_Real rownorm; int j; int nnonz; dualsol = SCIProwGetDualsol(rows[i]); if( SCIPisFeasPositive(scip, dualsol) ) factor = 1.0; else if( SCIPisFeasNegative(scip, dualsol) ) factor = -1.0; else continue; /* get the row's data */ coeffs = SCIProwGetVals(rows[i]); cols = SCIProwGetCols(rows[i]); nnonz = SCIProwGetNNonz(rows[i]); rownorm = 0.0; for( j = nnonz - 1; j >= 0; --j ) { SCIP_VAR* var; var = SCIPcolGetVar(cols[j]); if( fracspace[SCIPvarGetProbindex(var)] >= 0 ) rownorm += coeffs[j] * coeffs[j]; } if( SCIPisFeasZero(scip,rownorm) ) continue; else { assert(rownorm > 0); rownorm = SQRT(rownorm); } for( j = nnonz - 1; j >= 0; --j ) { SCIP_VAR* var; int f; var = SCIPcolGetVar(cols[j]); f = fracspace[SCIPvarGetProbindex(var)]; if( f >= 0 ) { raydirection[f] += factor * coeffs[j] / rownorm; assert(SCIP_REAL_MIN <= raydirection[f] && raydirection[f] <= SCIP_REAL_MAX); } } } return SCIP_OKAY; }
/** generates the direction of the shooting ray as the average of the extreme rays of the basic cone */ static SCIP_RETCODE generateAverageRay( SCIP* scip, /**< SCIP data structure */ SCIP_Real* raydirection, /**< shooting ray */ SCIP_VAR** subspacevars, /**< pointer to fractional space variables */ int nsubspacevars, /**< dimension of fractional space */ SCIP_Bool weighted /**< should the rays be weighted? */ ) { SCIP_ROW** rows; SCIP_Real** tableaurows; SCIP_Real* rownorm; SCIP_Real rowweight; int nrows; int i; int j; assert(scip != NULL); assert(raydirection != NULL); assert(subspacevars != NULL); /* get data */ SCIP_CALL( SCIPgetLPRowsData(scip, &rows, &nrows) ); /* allocate memory */ SCIP_CALL( SCIPallocBufferArray(scip, &tableaurows, nsubspacevars) ); for( j = nsubspacevars - 1; j >= 0; --j ) { /*lint --e{866}*/ SCIP_CALL( SCIPallocBufferArray(scip, &tableaurows[j], nrows) ); } SCIP_CALL( SCIPallocBufferArray(scip, &rownorm, nrows) ); for( i = nrows - 1; i >= 0; --i ) rownorm[i] = 0; /* get the relevant columns of the simplex tableau */ for( j = nsubspacevars-1; j >= 0; --j ) { assert(SCIPcolGetLPPos(SCIPvarGetCol(subspacevars[j])) >= 0); SCIP_CALL( SCIPgetLPBInvACol(scip, SCIPcolGetLPPos(SCIPvarGetCol(subspacevars[j])), tableaurows[j]) ); for( i = nrows - 1; i >= 0; --i ) rownorm[i] += tableaurows[j][i] * tableaurows[j][i]; } /* take average over all rows of the tableau */ for( i = nrows - 1; i >= 0; --i ) { if( SCIPisFeasZero(scip, rownorm[i]) ) continue; else rownorm[i] = SQRT(rownorm[i]); rowweight = 0.0; if( weighted ) { rowweight = SCIProwGetDualsol(rows[i]); if( SCIPisFeasZero(scip, rowweight) ) continue; } else rowweight = 1.0; for( j = nsubspacevars - 1; j >= 0; --j ) { raydirection[j] += tableaurows[j][i] / (rownorm[i] * rowweight); assert(SCIP_REAL_MIN <= raydirection[j] && raydirection[j] <= SCIP_REAL_MAX); } } /* free memory */ SCIPfreeBufferArray(scip, &rownorm); for( j = nsubspacevars - 1; j >= 0; --j ) { SCIPfreeBufferArray(scip, &tableaurows[j]); } SCIPfreeBufferArray(scip, &tableaurows); return SCIP_OKAY; }