/** adds cut to separation storage and captures it; * if the cut should be forced to enter the LP, an infinite score has to be used */ SCIP_RETCODE SCIPsepastoreAddCut( SCIP_SEPASTORE* sepastore, /**< separation storage */ BMS_BLKMEM* blkmem, /**< block memory */ SCIP_SET* set, /**< global SCIP settings */ SCIP_STAT* stat, /**< problem statistics data */ SCIP_EVENTQUEUE* eventqueue, /**< event queue */ SCIP_EVENTFILTER* eventfilter, /**< event filter for global events */ SCIP_LP* lp, /**< LP data */ SCIP_SOL* sol, /**< primal solution that was separated, or NULL for LP solution */ SCIP_ROW* cut, /**< separated cut */ SCIP_Bool forcecut, /**< should the cut be forced to enter the LP? */ SCIP_Bool root /**< are we at the root node? */ ) { assert(sepastore != NULL); assert(cut != NULL); assert(!SCIProwIsInLP(cut)); assert(!SCIPsetIsInfinity(set, -SCIProwGetLhs(cut)) || !SCIPsetIsInfinity(set, SCIProwGetRhs(cut))); /* debug: check cut for feasibility */ SCIP_CALL( SCIPdebugCheckRow(set, cut) ); /*lint !e506 !e774*/ /* update statistics of total number of found cuts */ if( !sepastore->initiallp ) { sepastore->ncutsfound++; sepastore->ncutsfoundround++; } /* add LP row cut to separation storage */ SCIP_CALL( sepastoreAddCut(sepastore, blkmem, set, stat, eventqueue, eventfilter, lp, sol, cut, forcecut, root) ); return SCIP_OKAY; }
/** checks cut for redundancy due to activity bounds */ static SCIP_Bool sepastoreIsCutRedundant( SCIP_SEPASTORE* sepastore, /**< separation storage */ SCIP_SET* set, /**< global SCIP settings */ SCIP_STAT* stat, /**< problem statistics data */ SCIP_ROW* cut /**< separated cut */ ) { SCIP_Real minactivity; SCIP_Real maxactivity; SCIP_Real lhs; SCIP_Real rhs; assert(sepastore != NULL); assert(cut != NULL); /* modifiable cuts cannot be declared redundant, since we don't know all coefficients */ if( SCIProwIsModifiable(cut) ) return FALSE; /* check for activity redundancy */ lhs = SCIProwGetLhs(cut); rhs = SCIProwGetRhs(cut); minactivity = SCIProwGetMinActivity(cut, set, stat); maxactivity = SCIProwGetMaxActivity(cut, set, stat); if( SCIPsetIsLE(set, lhs, minactivity) && SCIPsetIsLE(set, maxactivity, rhs) ) { SCIPdebugMessage("ignoring activity redundant cut <%s> (sides=[%g,%g], act=[%g,%g]\n", SCIProwGetName(cut), lhs, rhs, minactivity, maxactivity); /*SCIPdebug(SCIProwPrint(cut, NULL));*/ return TRUE; } return FALSE; }
/** 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; }
/** update row violation arrays after a row's activity value changed */ static void updateViolations( SCIP* scip, /**< SCIP data structure */ SCIP_ROW* row, /**< LP row */ SCIP_ROW** violrows, /**< array with currently violated rows */ int* violrowpos, /**< position of LP rows in violrows array */ int* nviolrows, /**< pointer to the number of currently violated rows */ SCIP_Real oldactivity, /**< old activity value of LP row */ SCIP_Real newactivity /**< new activity value of LP row */ ) { SCIP_Real lhs; SCIP_Real rhs; SCIP_Bool oldviol; SCIP_Bool newviol; assert(violrows != NULL); assert(violrowpos != NULL); assert(nviolrows != NULL); lhs = SCIProwGetLhs(row); rhs = SCIProwGetRhs(row); oldviol = (SCIPisFeasLT(scip, oldactivity, lhs) || SCIPisFeasGT(scip, oldactivity, rhs)); newviol = (SCIPisFeasLT(scip, newactivity, lhs) || SCIPisFeasGT(scip, newactivity, rhs)); if( oldviol != newviol ) { int rowpos; int violpos; rowpos = SCIProwGetLPPos(row); assert(rowpos >= 0); if( oldviol ) { /* the row violation was repaired: remove row from violrows array, decrease violation count */ violpos = violrowpos[rowpos]; assert(0 <= violpos && violpos < *nviolrows); assert(violrows[violpos] == row); violrowpos[rowpos] = -1; if( violpos != *nviolrows-1 ) { violrows[violpos] = violrows[*nviolrows-1]; violrowpos[SCIProwGetLPPos(violrows[violpos])] = violpos; } (*nviolrows)--; } else { /* the row is now violated: add row to violrows array, increase violation count */ assert(violrowpos[rowpos] == -1); violrows[*nviolrows] = row; violrowpos[rowpos] = *nviolrows; (*nviolrows)++; } } }
/** 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; }
/** compute value by which the solution of variable @p var can be shifted */ static SCIP_Real calcShiftVal( SCIP* scip, /**< SCIP data structure */ SCIP_VAR* var, /**< variable that should be shifted */ SCIP_Real solval, /**< current solution value */ SCIP_Real* activities /**< LP row activities */ ) { SCIP_Real lb; SCIP_Real ub; SCIP_Real obj; SCIP_Real shiftval; SCIP_COL* col; SCIP_ROW** colrows; SCIP_Real* colvals; SCIP_Bool shiftdown; int ncolrows; int i; /* get variable's solution value, global bounds and objective coefficient */ lb = SCIPvarGetLbGlobal(var); ub = SCIPvarGetUbGlobal(var); obj = SCIPvarGetObj(var); shiftval = 0.0; shiftdown = TRUE; /* determine shifting direction and maximal possible shifting w.r.t. corresponding bound */ if( obj > 0.0 && SCIPisFeasGE(scip, solval - 1.0, lb) ) shiftval = SCIPfeasFloor(scip, solval - lb); else if( obj < 0.0 && SCIPisFeasLE(scip, solval + 1.0, ub) ) { shiftval = SCIPfeasFloor(scip, ub - solval); shiftdown = FALSE; } else return 0.0; SCIPdebugMessage("Try to shift %s variable <%s> with\n", shiftdown ? "down" : "up", SCIPvarGetName(var) ); SCIPdebugMessage(" lb:<%g> <= val:<%g> <= ub:<%g> and obj:<%g> by at most: <%g>\n", lb, solval, ub, obj, shiftval); /* get data of LP column */ col = SCIPvarGetCol(var); colrows = SCIPcolGetRows(col); colvals = SCIPcolGetVals(col); ncolrows = SCIPcolGetNLPNonz(col); assert(ncolrows == 0 || (colrows != NULL && colvals != NULL)); /* find minimal shift value, st. all rows stay valid */ for( i = 0; i < ncolrows && shiftval > 0.0; ++i ) { SCIP_ROW* row; int rowpos; row = colrows[i]; rowpos = SCIProwGetLPPos(row); assert(-1 <= rowpos && rowpos < SCIPgetNLPRows(scip) ); /* only global rows need to be valid */ if( rowpos >= 0 && !SCIProwIsLocal(row) ) { SCIP_Real shiftvalrow; assert(SCIProwIsInLP(row)); if( shiftdown == (colvals[i] > 0) ) shiftvalrow = SCIPfeasFloor(scip, (activities[rowpos] - SCIProwGetLhs(row)) / ABS(colvals[i])); else shiftvalrow = SCIPfeasFloor(scip, (SCIProwGetRhs(row) - activities[rowpos]) / ABS(colvals[i])); #ifdef SCIP_DEBUG if( shiftvalrow < shiftval ) { SCIPdebugMessage(" -> The shift value had to be reduced to <%g>, because of row <%s>.\n", shiftvalrow, SCIProwGetName(row)); SCIPdebugMessage(" lhs:<%g> <= act:<%g> <= rhs:<%g>, colval:<%g>\n", SCIProwGetLhs(row), activities[rowpos], SCIProwGetRhs(row), colvals[i]); } #endif shiftval = MIN(shiftval, shiftvalrow); /* shiftvalrow might be negative, if we detected infeasibility -> make sure that shiftval is >= 0 */ shiftval = MAX(shiftval, 0.0); } } if( shiftdown ) shiftval *= -1.0; /* we must not shift variables to infinity */ if( SCIPisInfinity(scip, solval + shiftval) ) shiftval = 0.0; return shiftval; }
/** returns a score value for the given variable based on the active constraints that the variable appears in */ static SCIP_Real getNActiveConsScore( SCIP* scip, /**< SCIP data structure */ SCIP_SOL* sol, /**< working solution */ SCIP_VAR* var, /**< variable to get the score value for */ SCIP_Real* downscore, /**< pointer to store the score for branching downwards */ SCIP_Real* upscore /**< pointer to store the score for branching upwards */ ) { SCIP_COL* col; SCIP_ROW** rows; SCIP_Real* vals; int nrows; int r; int nactrows; SCIP_Real nlprows; SCIP_Real downcoefsum; SCIP_Real upcoefsum; SCIP_Real score; assert(downscore != NULL); assert(upscore != NULL); *downscore = 0.0; *upscore = 0.0; if( SCIPvarGetStatus(var) != SCIP_VARSTATUS_COLUMN ) return 0.0; col = SCIPvarGetCol(var); assert(col != NULL); rows = SCIPcolGetRows(col); vals = SCIPcolGetVals(col); nrows = SCIPcolGetNLPNonz(col); nactrows = 0; downcoefsum = 0.0; upcoefsum = 0.0; for( r = 0; r < nrows; ++r ) { SCIP_ROW* row; SCIP_Real activity; SCIP_Real lhs; SCIP_Real rhs; SCIP_Real dualsol; row = rows[r]; /* calculate number of active constraint sides, i.e., count equations as two */ lhs = SCIProwGetLhs(row); rhs = SCIProwGetRhs(row); /* @todo this is suboptimal because activity is calculated by looping over all nonzeros of this row, need to * store LP activities instead (which cannot be retrieved if no LP was solved at this node) */ activity = SCIPgetRowSolActivity(scip, row, sol); dualsol = SCIProwGetDualsol(row); if( SCIPisFeasEQ(scip, activity, lhs) ) { SCIP_Real coef; nactrows++; coef = vals[r] / SCIProwGetNorm(row); if( SCIPisFeasPositive(scip, dualsol) ) { if( coef > 0.0 ) downcoefsum += coef; else upcoefsum -= coef; } } else if( SCIPisFeasEQ(scip, activity, rhs) ) { SCIP_Real coef; nactrows++; coef = vals[r] / SCIProwGetNorm(row); if( SCIPisFeasNegative(scip, dualsol) ) { if( coef > 0.0 ) upcoefsum += coef; else downcoefsum -= coef; } } } /* use the number of LP rows for normalization */ nlprows = (SCIP_Real)SCIPgetNLPRows(scip); upcoefsum /= nlprows; downcoefsum /= nlprows; /* calculate the score using SCIP's branch score. Pass NULL as variable to not have the var branch factor influence * the result */ score = nactrows / nlprows + SCIPgetBranchScore(scip, NULL, downcoefsum, upcoefsum); assert(score <= 3.0); assert(score >= 0.0); *downscore = downcoefsum; *upscore = upcoefsum; return score; }
/** 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; }
/** returns a score value for the given variable based on the active constraints that the variable appears in */ static SCIP_Real getNActiveConsScore( SCIP* scip, /**< SCIP data structure */ SCIP_VAR* var, /**< variable to get the score value for */ SCIP_Real* downscore, /**< pointer to store the score for branching downwards */ SCIP_Real* upscore /**< pointer to store the score for branching upwards */ ) { SCIP_COL* col; SCIP_ROW** rows; SCIP_Real* vals; int nrows; int r; int nactrows; SCIP_Real downcoefsum; SCIP_Real upcoefsum; SCIP_Real score; assert(downscore != NULL); assert(upscore != NULL); *downscore = 0.0; *upscore = 0.0; if( SCIPvarGetStatus(var) != SCIP_VARSTATUS_COLUMN ) return 0.0; col = SCIPvarGetCol(var); assert(col != NULL); rows = SCIPcolGetRows(col); vals = SCIPcolGetVals(col); nrows = SCIPcolGetNLPNonz(col); nactrows = 0; downcoefsum = 0.0; upcoefsum = 0.0; for( r = 0; r < nrows; ++r ) { SCIP_Real activity; SCIP_Real lhs; SCIP_Real rhs; SCIP_Real dualsol; /* calculate number of active constraint sides, i.e., count equations as two */ lhs = SCIProwGetLhs(rows[r]); rhs = SCIProwGetRhs(rows[r]); activity = SCIPgetRowLPActivity(scip, rows[r]); dualsol = SCIProwGetDualsol(rows[r]); if( SCIPisFeasEQ(scip, activity, lhs) ) { SCIP_Real coef; nactrows++; coef = vals[r] / SCIProwGetNorm(rows[r]); if( SCIPisFeasPositive(scip, dualsol) ) { if( coef > 0.0 ) downcoefsum += coef; else upcoefsum -= coef; } } else if( SCIPisFeasEQ(scip, activity, rhs) ) { SCIP_Real coef; nactrows++; coef = vals[r] / SCIProwGetNorm(rows[r]); if( SCIPisFeasNegative(scip, dualsol) ) { if( coef > 0.0 ) upcoefsum += coef; else downcoefsum -= coef; } } } score = 1e-3*nactrows + (downcoefsum + 1e-6) * (upcoefsum + 1e-6); *downscore = -downcoefsum; *upscore = -upcoefsum; return score; }
/** when a variable is shifted, the activities and slacks of all rows it appears in have to be updated */ static SCIP_RETCODE updateSlacks( SCIP* scip, /**< pointer to current SCIP data structure */ SCIP_SOL* sol, /**< working solution */ SCIP_VAR* var, /**< pointer to variable to be modified */ SCIP_Real shiftvalue, /**< the value by which the variable is shifted */ SCIP_Real* upslacks, /**< upslacks of all rows the variable appears in */ SCIP_Real* downslacks, /**< downslacks of all rows the variable appears in */ SCIP_Real* activities, /**< activities of the LP rows */ SCIP_VAR** slackvars, /**< the slack variables for equality rows */ SCIP_Real* slackcoeffs, /**< the slack variable coefficients */ int nslacks /**< size of the arrays */ ) { SCIP_COL* col; /* the corresponding column of variable var */ SCIP_ROW** rows; /* pointer to the nonzero coefficient rows for variable var */ int nrows; /* the number of nonzeros */ SCIP_Real* colvals; /* array to store the nonzero coefficients */ int i; assert(scip != NULL); assert(sol != NULL); assert(var != NULL); assert(upslacks != NULL); assert(downslacks != NULL); assert(activities != NULL); assert(nslacks >= 0); col = SCIPvarGetCol(var); assert(col != NULL); rows = SCIPcolGetRows(col); nrows = SCIPcolGetNLPNonz(col); colvals = SCIPcolGetVals(col); assert(nrows == 0 || (rows != NULL && colvals != NULL)); /* go through all rows the shifted variable appears in */ for( i = 0; i < nrows; ++i ) { int rowpos; rowpos = SCIProwGetLPPos(rows[i]); assert(-1 <= rowpos && rowpos < nslacks); /* if the row is in the LP, update its activity, up and down slack */ if( rowpos >= 0 ) { SCIP_Real val; val = colvals[i] * shiftvalue; /* if the row is an equation, we update its slack variable instead of its activities */ if( SCIPisFeasEQ(scip, SCIProwGetLhs(rows[i]), SCIProwGetRhs(rows[i])) ) { SCIP_Real slackvarshiftval; SCIP_Real slackvarsolval; assert(slackvars[rowpos] != NULL); assert(!SCIPisFeasZero(scip, slackcoeffs[rowpos])); slackvarsolval = SCIPgetSolVal(scip, sol, slackvars[rowpos]); slackvarshiftval = -val / slackcoeffs[rowpos]; assert(SCIPisFeasGE(scip, slackvarsolval + slackvarshiftval, SCIPvarGetLbGlobal(slackvars[rowpos]))); assert(SCIPisFeasLE(scip, slackvarsolval + slackvarshiftval, SCIPvarGetUbGlobal(slackvars[rowpos]))); SCIP_CALL( SCIPsetSolVal(scip, sol, slackvars[rowpos], slackvarsolval + slackvarshiftval) ); } else if( !SCIPisInfinity(scip, -activities[rowpos]) && !SCIPisInfinity(scip, activities[rowpos]) ) activities[rowpos] += val; /* the slacks of the row now can be updated independently of its type */ if( !SCIPisInfinity(scip, upslacks[rowpos]) ) upslacks[rowpos] -= val; if( !SCIPisInfinity(scip, -downslacks[rowpos]) ) downslacks[rowpos] += val; assert(!SCIPisFeasNegative(scip, upslacks[rowpos])); assert(!SCIPisFeasNegative(scip, downslacks[rowpos])); } } return SCIP_OKAY; }
/** gets the nonbasic coefficients of a simplex row */ static SCIP_RETCODE getSimplexCoefficients( SCIP* scip, /**< SCIP pointer */ SCIP_ROW** rows, /**< LP rows */ int nrows, /**< number LP rows */ SCIP_COL** cols, /**< LP columns */ int ncols, /**< number of LP columns */ SCIP_Real* coef, /**< row of \f$B^{-1} \cdot A\f$ */ SCIP_Real* binvrow, /**< row of \f$B^{-1}\f$ */ SCIP_Real* simplexcoefs, /**< pointer to store the nonbasic simplex-coefficients */ int* nonbasicnumber /**< pointer to store the number of nonbasic simplex-coefficients */ ) { int r; int c; assert( scip != NULL ); assert( rows != NULL ); assert( nonbasicnumber != NULL ); assert( simplexcoefs != NULL ); assert( cols != NULL ); *nonbasicnumber = 0; /* note: the non-slack variables have to be added first (see the function generateDisjCutSOS1()) */ /* get simplex-coefficients of the non-basic non-slack variables */ for (c = 0; c < ncols; ++c) { SCIP_COL* col; col = cols[c]; assert( col != NULL ); if ( SCIPcolGetBasisStatus(col) == SCIP_BASESTAT_LOWER || SCIPcolGetBasisStatus(col) == SCIP_BASESTAT_UPPER ) simplexcoefs[(*nonbasicnumber)++] = coef[c]; } /* get simplex-coefficients of the non-basic slack variables */ for (r = 0; r < nrows; ++r) { SCIP_ROW* row; row = rows[r]; assert( row != NULL ); if ( SCIProwGetBasisStatus(row) == SCIP_BASESTAT_LOWER || SCIProwGetBasisStatus(row) == SCIP_BASESTAT_UPPER ) { assert( SCIPisFeasZero(scip, SCIPgetRowLPActivity(scip, row) - SCIProwGetRhs(row)) || SCIPisFeasZero(scip, SCIPgetRowLPActivity(scip, row) - SCIProwGetLhs(row)) ); simplexcoefs[(*nonbasicnumber)++] = binvrow[r]; } } return SCIP_OKAY; }
SCIP_RETCODE SCIPconshdlrBenders::sepaBenders( SCIP * scip, SCIP_CONSHDLR * conshdlr, SCIP_SOL * sol, whereFrom where, SCIP_RESULT * result) { OsiCuts cs; /**< Benders cut placeholder */ SCIP_Real * vals = NULL; /**< current solution */ #if 1 if (scip_checkpriority_ < 0) { /** consider incumbent solutions only */ double primObj = SCIPgetPrimalbound(scip); double currObj = SCIPgetSolOrigObj(scip, sol); if (SCIPisLT(scip, primObj, currObj)) { DSPdebugMessage(" -> primObj %e currObj %e\n", primObj, currObj); return SCIP_OKAY; } } #endif /** allocate memory */ SCIP_CALL(SCIPallocMemoryArray(scip, &vals, nvars_)); /** get current solution */ SCIP_CALL(SCIPgetSolVals(scip, sol, nvars_, vars_, vals)); /** TODO The following filter does not work, meaning that it provides suboptimal solution. * I do not know the reason. */ #if 0 double maxviol = 1.e-10; for (int j = 0; j < nvars_ - naux_; ++j) { SCIP_VARTYPE vartype = SCIPvarGetType(vars_[j]); if (vartype == SCIP_VARTYPE_CONTINUOUS) continue; double viol = 0.5 - fabs(vals[j] - floor(vals[j]) - 0.5); if (viol > maxviol) maxviol = viol; } DSPdebugMessage("maximum violation %e\n", maxviol); if (where != from_scip_check && where != from_scip_enfolp && where != from_scip_enfops && maxviol > 1.e-7) { printf("where %d maxviol %e\n", where, maxviol); /** free memory */ SCIPfreeMemoryArray(scip, &vals); return SCIP_OKAY; } #endif #ifdef DSP_DEBUG2 double minvals = COIN_DBL_MAX; double maxvals = -COIN_DBL_MAX; double sumvals = 0.; double ssvals = 0.; //printf("nvars_ %d naux_ %d nAuxvars_ %d\n", nvars_, naux_, tss_->nAuxvars_); for (int j = 0; j < nvars_ - naux_; ++j) { // if (vals[j] < 0 || vals[j] > 1) // printf("solution %d has value %e.\n", j, vals[j]); sumvals += vals[j]; ssvals += vals[j] * vals[j]; minvals = minvals > vals[j] ? vals[j] : minvals; maxvals = maxvals < vals[j] ? vals[j] : maxvals; } DSPdebugMessage("solution: min %e max %e avg %e sum %e two-norm %e\n", minvals, maxvals, sumvals / nvars_, sumvals, sqrt(ssvals)); #endif #define SCAN_GLOBAL_CUT_POOL #ifdef SCAN_GLOBAL_CUT_POOL if (SCIPgetStage(scip) == SCIP_STAGE_SOLVING || SCIPgetStage(scip) == SCIP_STAGE_SOLVED || SCIPgetStage(scip) == SCIP_STAGE_EXITSOLVE) { bool addedPoolCut = false; int numPoolCuts = SCIPgetNPoolCuts(scip); int numCutsToScan = 100; SCIP_CUT ** poolcuts = SCIPgetPoolCuts(scip); for (int i = numPoolCuts - 1; i >= 0; --i) { if (i < 0) break; if (numCutsToScan == 0) break; /** retrieve row */ SCIP_ROW * poolcutrow = SCIPcutGetRow(poolcuts[i]); /** benders? */ if (strcmp(SCIProwGetName(poolcutrow), "benders") != 0) continue; /** counter */ numCutsToScan--; if (SCIPgetCutEfficacy(scip, sol, poolcutrow) > 1.e-6) { if (where == from_scip_sepalp || where == from_scip_sepasol || where == from_scip_enfolp) { /** add cut */ SCIP_Bool infeasible; SCIP_CALL(SCIPaddCut(scip, sol, poolcutrow, FALSE, /**< force cut */ &infeasible)); if (infeasible) *result = SCIP_CUTOFF; else //if (*result != SCIP_CUTOFF) *result = SCIP_SEPARATED; } else *result = SCIP_INFEASIBLE; addedPoolCut = true; break; } } if (addedPoolCut) { DSPdebugMessage("Added pool cut\n"); /** free memory */ SCIPfreeMemoryArray(scip, &vals); return SCIP_OKAY; } } #endif /** generate Benders cuts */ assert(tss_); tss_->generateCuts(nvars_, vals, &cs); /** If found Benders cuts */ for (int i = 0; i < cs.sizeCuts(); ++i) { /** get cut pointer */ OsiRowCut * rc = cs.rowCutPtr(i); if (!rc) continue; const CoinPackedVector cutrow = rc->row(); if (cutrow.getNumElements() == 0) continue; /** is optimality cut? */ bool isOptimalityCut = false; for (int j = nvars_ - naux_; j < nvars_; ++j) { if (cutrow.getMaxIndex() == j) { isOptimalityCut = true; break; } } double efficacy = rc->violated(vals) / cutrow.twoNorm(); SCIP_Bool isEfficacious = efficacy > 1.e-6; #define KK_TEST #ifdef KK_TEST if (SCIPgetStage(scip) == SCIP_STAGE_INITSOLVE || SCIPgetStage(scip) == SCIP_STAGE_SOLVING) { /** create empty row */ SCIP_ROW * row = NULL; SCIP_CALL(SCIPcreateEmptyRowCons(scip, &row, conshdlr, "benders", rc->lb(), SCIPinfinity(scip), FALSE, /**< is row local? */ FALSE, /**< is row modifiable? */ FALSE /**< is row removable? can this be TRUE? */)); /** cache the row extension and only flush them if the cut gets added */ SCIP_CALL(SCIPcacheRowExtensions(scip, row)); /** collect all non-zero coefficients */ for (int j = 0; j < cutrow.getNumElements(); ++j) SCIP_CALL(SCIPaddVarToRow(scip, row, vars_[cutrow.getIndices()[j]], cutrow.getElements()[j])); DSPdebugMessage("found Benders (%s) cut: act=%f, lhs=%f, norm=%f, eff=%f, min=%f, max=%f (range=%f)\n", isOptimalityCut ? "opti" : "feas", SCIPgetRowLPActivity(scip, row), SCIProwGetLhs(row), SCIProwGetNorm(row), SCIPgetCutEfficacy(scip, sol, row), SCIPgetRowMinCoef(scip, row), SCIPgetRowMaxCoef(scip, row), SCIPgetRowMaxCoef(scip, row)/SCIPgetRowMinCoef(scip, row)); /** flush all changes before adding cut */ SCIP_CALL(SCIPflushRowExtensions(scip, row)); DSPdebugMessage("efficacy %e isEfficatious %d\n", efficacy, isEfficacious); if (isEfficacious) { if (where == from_scip_sepalp || where == from_scip_sepasol || where == from_scip_enfolp) { /** add cut */ SCIP_Bool infeasible; SCIP_CALL(SCIPaddCut(scip, sol, row, FALSE, /**< force cut */ &infeasible)); if (infeasible) *result = SCIP_CUTOFF; else //if (*result != SCIP_CUTOFF) *result = SCIP_SEPARATED; } else *result = SCIP_INFEASIBLE; } /** add cut to global pool */ SCIP_CALL(SCIPaddPoolCut(scip, row)); DSPdebugMessage("number of cuts in global cut pool: %d\n", SCIPgetNPoolCuts(scip)); /** release the row */ SCIP_CALL(SCIPreleaseRow(scip, &row)); } else if (isEfficacious && where != from_scip_sepalp && where != from_scip_sepasol && where != from_scip_enfolp) *result = SCIP_INFEASIBLE; #else if (where == from_scip_sepalp || where == from_scip_sepasol || where == from_scip_enfolp) { /** create empty row */ SCIP_ROW * row = NULL; SCIP_CALL(SCIPcreateEmptyRowCons(scip, &row, conshdlr, "benders", rc->lb(), SCIPinfinity(scip), FALSE, /**< is row local? */ FALSE, /**< is row modifiable? */ FALSE /**< is row removable? can this be TRUE? */)); /** cache the row extension and only flush them if the cut gets added */ SCIP_CALL(SCIPcacheRowExtensions(scip, row)); /** collect all non-zero coefficients */ for (int j = 0; j < cutrow.getNumElements(); ++j) SCIP_CALL(SCIPaddVarToRow(scip, row, vars_[cutrow.getIndices()[j]], cutrow.getElements()[j])); DSPdebugMessage("found Benders (%s) cut: act=%f, lhs=%f, norm=%f, eff=%f, min=%f, max=%f (range=%f)\n", isOptimalityCut ? "opti" : "feas", SCIPgetRowLPActivity(scip, row), SCIProwGetLhs(row), SCIProwGetNorm(row), SCIPgetCutEfficacy(scip, NULL, row), SCIPgetRowMinCoef(scip, row), SCIPgetRowMaxCoef(scip, row), SCIPgetRowMaxCoef(scip, row)/SCIPgetRowMinCoef(scip, row)); /** flush all changes before adding cut */ SCIP_CALL(SCIPflushRowExtensions(scip, row)); /** is cut efficacious? */ if (isOptimalityCut) { efficacy = SCIPgetCutEfficacy(scip, sol, row); isEfficacious = SCIPisCutEfficacious(scip, sol, row); } else { efficacy = rc->violated(vals); isEfficacious = efficacy > 1.e-6; } if (isEfficacious) { /** add cut */ SCIP_Bool infeasible; SCIP_CALL(SCIPaddCut(scip, sol, row, FALSE, /**< force cut */ &infeasible)); if (infeasible) *result = SCIP_CUTOFF; else if (*result != SCIP_CUTOFF) *result = SCIP_SEPARATED; } /** add cut to global pool */ SCIP_CALL(SCIPaddPoolCut(scip, row)); /** release the row */ SCIP_CALL(SCIPreleaseRow(scip, &row)); } else { if (isOptimalityCut) { efficacy = rc->violated(vals) / cutrow.twoNorm(); isEfficacious = efficacy > 0.05; } else { efficacy = rc->violated(vals); isEfficacious = efficacy > 1.e-6; } DSPdebugMessage("%s efficacy %e\n", isOptimalityCut ? "Opti" : "Feas", efficacy); if (isEfficacious == TRUE) *result = SCIP_INFEASIBLE; } #endif } /** free memory */ SCIPfreeMemoryArray(scip, &vals); 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; }
/** returns a variable, that pushes activity of the row in the given direction with minimal negative impact on other rows; * if variables have equal impact, chooses the one with best objective value improvement in corresponding direction; * prefer fractional integers over other variables in order to become integral during the process; * shifting in a direction is forbidden, if this forces the objective value over the upper bound, or if the variable * was already shifted in the opposite direction */ static SCIP_RETCODE selectShifting( SCIP* scip, /**< SCIP data structure */ SCIP_SOL* sol, /**< primal solution */ SCIP_ROW* row, /**< LP row */ SCIP_Real rowactivity, /**< activity of LP row */ int direction, /**< should the activity be increased (+1) or decreased (-1)? */ SCIP_Real* nincreases, /**< array with weighted number of increasings per variables */ SCIP_Real* ndecreases, /**< array with weighted number of decreasings per variables */ SCIP_Real increaseweight, /**< current weight of increase/decrease updates */ SCIP_VAR** shiftvar, /**< pointer to store the shifting variable, returns NULL if impossible */ SCIP_Real* oldsolval, /**< pointer to store old solution value of shifting variable */ SCIP_Real* newsolval /**< pointer to store new (shifted) solution value of shifting variable */ ) { SCIP_COL** rowcols; SCIP_Real* rowvals; int nrowcols; SCIP_Real activitydelta; SCIP_Real bestshiftscore; SCIP_Real bestdeltaobj; int c; assert(direction == +1 || direction == -1); assert(nincreases != NULL); assert(ndecreases != NULL); assert(shiftvar != NULL); assert(oldsolval != NULL); assert(newsolval != NULL); /* get row entries */ rowcols = SCIProwGetCols(row); rowvals = SCIProwGetVals(row); nrowcols = SCIProwGetNLPNonz(row); /* calculate how much the activity must be shifted in order to become feasible */ activitydelta = (direction == +1 ? SCIProwGetLhs(row) - rowactivity : SCIProwGetRhs(row) - rowactivity); assert((direction == +1 && SCIPisPositive(scip, activitydelta)) || (direction == -1 && SCIPisNegative(scip, activitydelta))); /* select shifting variable */ bestshiftscore = SCIP_REAL_MAX; bestdeltaobj = SCIPinfinity(scip); *shiftvar = NULL; *newsolval = 0.0; *oldsolval = 0.0; for( c = 0; c < nrowcols; ++c ) { SCIP_COL* col; SCIP_VAR* var; SCIP_Real val; SCIP_Real solval; SCIP_Real shiftval; SCIP_Real shiftscore; SCIP_Bool isinteger; SCIP_Bool isfrac; SCIP_Bool increase; col = rowcols[c]; var = SCIPcolGetVar(col); val = rowvals[c]; assert(!SCIPisZero(scip, val)); solval = SCIPgetSolVal(scip, sol, var); isinteger = (SCIPvarGetType(var) == SCIP_VARTYPE_BINARY || SCIPvarGetType(var) == SCIP_VARTYPE_INTEGER); isfrac = isinteger && !SCIPisFeasIntegral(scip, solval); increase = (direction * val > 0.0); /* calculate the score of the shifting (prefer smaller values) */ if( isfrac ) shiftscore = increase ? -1.0 / (SCIPvarGetNLocksUp(var) + 1.0) : -1.0 / (SCIPvarGetNLocksDown(var) + 1.0); else { int probindex; probindex = SCIPvarGetProbindex(var); if( increase ) shiftscore = ndecreases[probindex]/increaseweight; else shiftscore = nincreases[probindex]/increaseweight; if( isinteger ) shiftscore += 1.0; } if( shiftscore <= bestshiftscore ) { SCIP_Real deltaobj; if( !increase ) { /* shifting down */ assert(direction * val < 0.0); if( isfrac ) shiftval = SCIPfeasFloor(scip, solval); else { SCIP_Real lb; assert(activitydelta/val < 0.0); shiftval = solval + activitydelta/val; assert(shiftval <= solval); /* may be equal due to numerical digit erasement in the subtraction */ if( SCIPvarIsIntegral(var) ) shiftval = SCIPfeasFloor(scip, shiftval); lb = SCIPvarGetLbGlobal(var); shiftval = MAX(shiftval, lb); } } else { /* shifting up */ assert(direction * val > 0.0); if( isfrac ) shiftval = SCIPfeasCeil(scip, solval); else { SCIP_Real ub; assert(activitydelta/val > 0.0); shiftval = solval + activitydelta/val; assert(shiftval >= solval); /* may be equal due to numerical digit erasement in the subtraction */ if( SCIPvarIsIntegral(var) ) shiftval = SCIPfeasCeil(scip, shiftval); ub = SCIPvarGetUbGlobal(var); shiftval = MIN(shiftval, ub); } } if( SCIPisEQ(scip, shiftval, solval) ) continue; deltaobj = SCIPvarGetObj(var) * (shiftval - solval); if( shiftscore < bestshiftscore || deltaobj < bestdeltaobj ) { bestshiftscore = shiftscore; bestdeltaobj = deltaobj; *shiftvar = var; *oldsolval = solval; *newsolval = shiftval; } } } return SCIP_OKAY; }
/** checks whether given row is valid for the debugging solution */ SCIP_RETCODE SCIPdebugCheckRow( SCIP_SET* set, /**< global SCIP settings */ SCIP_ROW* row /**< row to check for validity */ ) { SCIP_COL** cols; SCIP_Real* vals; SCIP_Real lhs; SCIP_Real rhs; int nnonz; int i; SCIP_Real minactivity; SCIP_Real maxactivity; SCIP_Real solval; assert(set != NULL); assert(row != NULL); /* check if we are in the original problem and not in a sub MIP */ if( !isSolutionInMip(set) ) return SCIP_OKAY; /* check if the incumbent solution is at least as good as the debug solution, so we can stop to check the debug solution */ if( debugSolIsAchieved(set) ) return SCIP_OKAY; /* if the row is only locally valid, check whether the debugging solution is contained in the local subproblem */ if( SCIProwIsLocal(row) ) { SCIP_Bool solcontained; SCIP_CALL( isSolutionInNode(SCIPblkmem(set->scip), set, SCIPgetCurrentNode(set->scip), &solcontained) ); if( !solcontained ) return SCIP_OKAY; } cols = SCIProwGetCols(row); vals = SCIProwGetVals(row); nnonz = SCIProwGetNNonz(row); lhs = SCIProwGetLhs(row); rhs = SCIProwGetRhs(row); /* calculate row's activity on debugging solution */ minactivity = SCIProwGetConstant(row); maxactivity = minactivity; for( i = 0; i < nnonz; ++i ) { SCIP_VAR* var; /* get solution value of variable in debugging solution */ var = SCIPcolGetVar(cols[i]); SCIP_CALL( getSolutionValue(set, var, &solval) ); if( solval != SCIP_UNKNOWN ) /*lint !e777*/ { minactivity += vals[i] * solval; maxactivity += vals[i] * solval; } else if( vals[i] > 0.0 ) { minactivity += vals[i] * SCIPvarGetLbGlobal(var); maxactivity += vals[i] * SCIPvarGetUbGlobal(var); } else if( vals[i] < 0.0 ) { minactivity += vals[i] * SCIPvarGetUbGlobal(var); maxactivity += vals[i] * SCIPvarGetLbGlobal(var); } } SCIPdebugMessage("debugging solution on row <%s>: %g <= [%g,%g] <= %g\n", SCIProwGetName(row), lhs, minactivity, maxactivity, rhs); /* check row for violation */ if( SCIPsetIsFeasLT(set, maxactivity, lhs) || SCIPsetIsFeasGT(set, minactivity, rhs) ) { printf("***** debug: row <%s> violates debugging solution (lhs=%.15g, rhs=%.15g, activity=[%.15g,%.15g], local=%d)\n", SCIProwGetName(row), lhs, rhs, minactivity, maxactivity, SCIProwIsLocal(row)); SCIProwPrint(row, NULL); /* output row with solution values */ printf("\n\n"); printf("***** debug: violated row <%s>:\n", SCIProwGetName(row)); printf(" %.15g <= %.15g", lhs, SCIProwGetConstant(row)); for( i = 0; i < nnonz; ++i ) { /* get solution value of variable in debugging solution */ SCIP_CALL( getSolutionValue(set, SCIPcolGetVar(cols[i]), &solval) ); printf(" %+.15g<%s>[%.15g]", vals[i], SCIPvarGetName(SCIPcolGetVar(cols[i])), solval); } printf(" <= %.15g\n", rhs); SCIPABORT(); } return SCIP_OKAY; }
/** computes a disjunctive cut inequality based on two simplex taubleau rows */ static SCIP_RETCODE generateDisjCutSOS1( SCIP* scip, /**< SCIP pointer */ SCIP_SEPA* sepa, /**< separator */ SCIP_ROW** rows, /**< LP rows */ int nrows, /**< number of LP rows */ SCIP_COL** cols, /**< LP columns */ int ncols, /**< number of LP columns */ int ndisjcuts, /**< number of disjunctive cuts found so far */ SCIP_Bool scale, /**< should cut be scaled */ SCIP_Bool strengthen, /**< should cut be strengthened if integer variables are present */ SCIP_Real cutlhs1, /**< left hand side of the first simplex row */ SCIP_Real cutlhs2, /**< left hand side of the second simplex row */ SCIP_Real bound1, /**< bound of first simplex row */ SCIP_Real bound2, /**< bound of second simplex row */ SCIP_Real* simplexcoefs1, /**< simplex coefficients of first row */ SCIP_Real* simplexcoefs2, /**< simplex coefficients of second row */ SCIP_Real* cutcoefs, /**< pointer to store cut coefficients (length: nscipvars) */ SCIP_ROW** row, /**< pointer to store disjunctive cut inequality */ SCIP_Bool* madeintegral /**< pointer to store whether cut has been scaled to integral values */ ) { char cutname[SCIP_MAXSTRLEN]; SCIP_COL** rowcols; SCIP_COL* col; SCIP_Real* rowvals; SCIP_Real lhsrow; SCIP_Real rhsrow; SCIP_Real cutlhs; SCIP_Real sgn; SCIP_Real lb; SCIP_Real ub; int nonbasicnumber = 0; int rownnonz; int ind; int r; int c; assert( scip != NULL ); assert( row != NULL ); assert( rows != NULL ); assert( cols != NULL ); assert( simplexcoefs1 != NULL ); assert( simplexcoefs2 != NULL ); assert( cutcoefs != NULL ); assert( sepa != NULL ); assert( madeintegral != NULL ); *madeintegral = FALSE; /* check signs */ if ( SCIPisFeasPositive(scip, cutlhs1) == SCIPisFeasPositive(scip, cutlhs2) ) sgn = 1.0; else sgn = -1.0; /* check bounds */ if ( SCIPisInfinity(scip, REALABS(bound1)) || SCIPisInfinity(scip, REALABS(bound2)) ) strengthen = FALSE; /* compute left hand side of row (a later update is possible, see below) */ cutlhs = sgn * cutlhs1 * cutlhs2; /* add cut-coefficients of the non-basic non-slack variables */ for (c = 0; c < ncols; ++c) { col = cols[c]; assert( col != NULL ); ind = SCIPcolGetLPPos(col); assert( ind >= 0 ); if ( SCIPcolGetBasisStatus(col) == SCIP_BASESTAT_LOWER ) { lb = SCIPcolGetLb(col); /* for integer variables we may obtain stronger coefficients */ if ( strengthen && SCIPcolIsIntegral(col) ) { SCIP_Real mval; SCIP_Real mvalfloor; SCIP_Real mvalceil; mval = (cutlhs2 * simplexcoefs1[nonbasicnumber] - cutlhs1 * simplexcoefs2[nonbasicnumber]) / (cutlhs2 * bound1 + cutlhs1 * bound2); mvalfloor = SCIPfloor(scip, mval); mvalceil = SCIPceil(scip, mval); cutcoefs[ind] = MIN(sgn * cutlhs2 * (simplexcoefs1[nonbasicnumber] - mvalfloor * bound1), sgn * cutlhs1 * (simplexcoefs2[nonbasicnumber] + mvalceil * bound2)); assert( SCIPisFeasLE(scip, cutcoefs[ind], MAX(sgn * cutlhs2 * simplexcoefs1[nonbasicnumber], sgn * cutlhs1 * simplexcoefs2[nonbasicnumber])) ); } else cutcoefs[ind] = MAX(sgn * cutlhs2 * simplexcoefs1[nonbasicnumber], sgn * cutlhs1 * simplexcoefs2[nonbasicnumber]); cutlhs += cutcoefs[ind] * lb; ++nonbasicnumber; } else if ( SCIPcolGetBasisStatus(col) == SCIP_BASESTAT_UPPER ) { ub = SCIPcolGetUb(col); /* for integer variables we may obtain stronger coefficients */ if ( strengthen && SCIPcolIsIntegral(col) ) { SCIP_Real mval; SCIP_Real mvalfloor; SCIP_Real mvalceil; mval = (cutlhs2 * simplexcoefs1[nonbasicnumber] - cutlhs1 * simplexcoefs2[nonbasicnumber]) / (cutlhs2 * bound1 + cutlhs1 * bound2); mvalfloor = SCIPfloor(scip, -mval); mvalceil = SCIPceil(scip, -mval); cutcoefs[ind] = MAX(sgn * cutlhs2 * (simplexcoefs1[nonbasicnumber] + mvalfloor * bound1), sgn * cutlhs1 * (simplexcoefs2[nonbasicnumber] - mvalceil * bound2)); assert( SCIPisFeasLE(scip, -cutcoefs[ind], -MIN(sgn * cutlhs2 * simplexcoefs1[nonbasicnumber], sgn * cutlhs1 * simplexcoefs2[nonbasicnumber])) ); } else cutcoefs[ind] = MIN(sgn * cutlhs2 * simplexcoefs1[nonbasicnumber], sgn * cutlhs1 * simplexcoefs2[nonbasicnumber]); cutlhs += cutcoefs[ind] * ub; ++nonbasicnumber; } else { assert( SCIPcolGetBasisStatus(col) != SCIP_BASESTAT_ZERO ); cutcoefs[ind] = 0.0; } } /* add cut-coefficients of the non-basic slack variables */ for (r = 0; r < nrows; ++r) { rhsrow = SCIProwGetRhs(rows[r]) - SCIProwGetConstant(rows[r]); lhsrow = SCIProwGetLhs(rows[r]) - SCIProwGetConstant(rows[r]); assert( SCIProwGetBasisStatus(rows[r]) != SCIP_BASESTAT_ZERO ); assert( SCIPisFeasZero(scip, lhsrow - rhsrow) || SCIPisNegative(scip, lhsrow - rhsrow) ); assert( SCIProwIsInLP(rows[r]) ); if ( SCIProwGetBasisStatus(rows[r]) != SCIP_BASESTAT_BASIC ) { SCIP_Real cutcoef; if ( SCIProwGetBasisStatus(rows[r]) == SCIP_BASESTAT_UPPER ) { assert( SCIPisFeasZero(scip, SCIPgetRowLPActivity(scip, rows[r]) - SCIProwGetRhs(rows[r])) ); cutcoef = MAX(sgn * cutlhs2 * simplexcoefs1[nonbasicnumber], sgn * cutlhs1 * simplexcoefs2[nonbasicnumber]); cutlhs -= cutcoef * rhsrow; ++nonbasicnumber; } else /* SCIProwGetBasisStatus(rows[r]) == SCIP_BASESTAT_LOWER */ { assert( SCIProwGetBasisStatus(rows[r]) == SCIP_BASESTAT_LOWER ); assert( SCIPisFeasZero(scip, SCIPgetRowLPActivity(scip, rows[r]) - SCIProwGetLhs(rows[r])) ); cutcoef = MIN(sgn * cutlhs2 * simplexcoefs1[nonbasicnumber], sgn * cutlhs1 * simplexcoefs2[nonbasicnumber]); cutlhs -= cutcoef * lhsrow; ++nonbasicnumber; } rownnonz = SCIProwGetNNonz(rows[r]); rowvals = SCIProwGetVals(rows[r]); rowcols = SCIProwGetCols(rows[r]); for (c = 0; c < rownnonz; ++c) { ind = SCIPcolGetLPPos(rowcols[c]); /* if column is not in LP, then return without generating cut */ if ( ind < 0 ) { *row = NULL; return SCIP_OKAY; } cutcoefs[ind] -= cutcoef * rowvals[c]; } } } /* create cut */ (void) SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "%s_%d_%d", SCIPsepaGetName(sepa), SCIPgetNLPs(scip), ndisjcuts); if ( SCIPgetDepth(scip) == 0 ) SCIP_CALL( SCIPcreateEmptyRowSepa(scip, row, sepa, cutname, cutlhs, SCIPinfinity(scip), FALSE, FALSE, TRUE) ); else SCIP_CALL( SCIPcreateEmptyRowSepa(scip, row, sepa, cutname, cutlhs, SCIPinfinity(scip), TRUE, FALSE, TRUE) ); SCIP_CALL( SCIPcacheRowExtensions(scip, *row) ); for (c = 0; c < ncols; ++c) { ind = SCIPcolGetLPPos(cols[c]); assert( ind >= 0 ); if ( ! SCIPisFeasZero(scip, cutcoefs[ind]) ) { SCIP_CALL( SCIPaddVarToRow(scip, *row, SCIPcolGetVar(cols[c]), cutcoefs[ind] ) ); } } SCIP_CALL( SCIPflushRowExtensions(scip, *row) ); /* try to scale the cut to integral values * @todo find better but still stable disjunctive cut settings */ if ( scale ) { int maxdepth; int depth; SCIP_Longint maxdnom; SCIP_Real maxscale; depth = SCIPgetDepth(scip); assert( depth >= 0 ); 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; } SCIP_CALL( SCIPmakeRowIntegral(scip, *row, -SCIPepsilon(scip), SCIPsumepsilon(scip), maxdnom, maxscale, TRUE, madeintegral) ); } return SCIP_OKAY; }
/** adds cut stored as LP row to separation storage and captures it; * if the cut should be forced to be used, an infinite score has to be used */ static SCIP_RETCODE sepastoreAddCut( SCIP_SEPASTORE* sepastore, /**< separation storage */ BMS_BLKMEM* blkmem, /**< block memory */ SCIP_SET* set, /**< global SCIP settings */ SCIP_STAT* stat, /**< problem statistics data */ SCIP_EVENTQUEUE* eventqueue, /**< event queue */ SCIP_EVENTFILTER* eventfilter, /**< event filter for global events */ SCIP_LP* lp, /**< LP data */ SCIP_SOL* sol, /**< primal solution that was separated, or NULL for LP solution */ SCIP_ROW* cut, /**< separated cut */ SCIP_Bool forcecut, /**< should the cut be forced to enter the LP? */ SCIP_Bool root /**< are we at the root node? */ ) { SCIP_Real cutefficacy; SCIP_Real cutobjparallelism; SCIP_Real cutscore; int pos; assert(sepastore != NULL); assert(sepastore->nforcedcuts <= sepastore->ncuts); assert(set != NULL); assert(cut != NULL); assert(sol != NULL || !SCIProwIsInLP(cut)); assert(!SCIPsetIsInfinity(set, -SCIProwGetLhs(cut)) || !SCIPsetIsInfinity(set, SCIProwGetRhs(cut))); assert(eventqueue != NULL); assert(eventfilter != NULL); /* in the root node, every local cut is a global cut, and global cuts are nicer in many ways...*/ if( root && SCIProwIsLocal(cut) ) { SCIPdebugMessage("change local flag of cut <%s> to FALSE due to addition in root node\n", SCIProwGetName(cut)); SCIP_CALL( SCIProwChgLocal(cut, FALSE) ); assert(!SCIProwIsLocal(cut)); } /* check cut for redundancy * in each separation round, make sure that at least one (even redundant) cut enters the LP to avoid cycling */ if( !forcecut && sepastore->ncuts > 0 && sepastoreIsCutRedundant(sepastore, set, stat, cut) ) return SCIP_OKAY; /* if only one cut is currently present in the cut store, it could be redundant; in this case, it can now be removed * again, because now a non redundant cut enters the store */ if( sepastore->ncuts == 1 && sepastoreIsCutRedundant(sepastore, set, stat, sepastore->cuts[0]) ) { /* check, if the row deletions from separation storage events are tracked * if so, issue ROWDELETEDSEPA event */ if( eventfilter->len > 0 && (eventfilter->eventmask & SCIP_EVENTTYPE_ROWDELETEDSEPA) != 0 ) { SCIP_EVENT* event; SCIP_CALL( SCIPeventCreateRowDeletedSepa(&event, blkmem, sepastore->cuts[0]) ); SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, NULL, NULL, eventfilter, &event) ); } SCIP_CALL( SCIProwRelease(&sepastore->cuts[0], blkmem, set, lp) ); sepastore->ncuts = 0; sepastore->nforcedcuts = 0; } /* a cut is forced to enter the LP if * - we construct the initial LP, or * - it has infinite score factor, or * - it is a bound change * if it is a non-forced cut and no cuts should be added, abort */ forcecut = forcecut || sepastore->initiallp || sepastore->forcecuts || (!SCIProwIsModifiable(cut) && SCIProwGetNNonz(cut) == 1); if( !forcecut && SCIPsetGetSepaMaxcuts(set, root) == 0 ) return SCIP_OKAY; /* get enough memory to store the cut */ SCIP_CALL( sepastoreEnsureCutsMem(sepastore, set, sepastore->ncuts+1) ); assert(sepastore->ncuts < sepastore->cutssize); if( forcecut ) { cutefficacy = SCIPsetInfinity(set); cutscore = SCIPsetInfinity(set); cutobjparallelism = 1.0; } else { /* initialize values to invalid (will be initialized during cut filtering) */ cutefficacy = SCIP_INVALID; cutscore = SCIP_INVALID; /* initialize parallelism to objective (constant throughout filtering) */ if( set->sepa_objparalfac > 0.0 ) cutobjparallelism = SCIProwGetObjParallelism(cut, set, lp); else cutobjparallelism = 0.0; /* no need to calculate it */ } SCIPdebugMessage("adding cut <%s> to separation storage of size %d (forcecut=%u, len=%d)\n", SCIProwGetName(cut), sepastore->ncuts, forcecut, SCIProwGetNNonz(cut)); /*SCIPdebug(SCIProwPrint(cut, NULL));*/ /* capture the cut */ SCIProwCapture(cut); /* add cut to arrays */ if( forcecut ) { /* make room at the beginning of the array for forced cut */ pos = sepastore->nforcedcuts; sepastore->cuts[sepastore->ncuts] = sepastore->cuts[pos]; sepastore->efficacies[sepastore->ncuts] = sepastore->efficacies[pos]; sepastore->objparallelisms[sepastore->ncuts] = sepastore->objparallelisms[pos]; sepastore->orthogonalities[sepastore->ncuts] = sepastore->orthogonalities[pos]; sepastore->scores[sepastore->ncuts] = sepastore->scores[pos]; sepastore->nforcedcuts++; } else pos = sepastore->ncuts; sepastore->cuts[pos] = cut; sepastore->efficacies[pos] = cutefficacy; sepastore->objparallelisms[pos] = cutobjparallelism; sepastore->orthogonalities[pos] = 1.0; sepastore->scores[pos] = cutscore; sepastore->ncuts++; /* check, if the row addition to separation storage events are tracked * if so, issue ROWADDEDSEPA event */ if( eventfilter->len > 0 && (eventfilter->eventmask & SCIP_EVENTTYPE_ROWADDEDSEPA) != 0 ) { SCIP_EVENT* event; SCIP_CALL( SCIPeventCreateRowAddedSepa(&event, blkmem, cut) ); SCIP_CALL( SCIPeventqueueAdd(eventqueue, blkmem, set, NULL, NULL, NULL, eventfilter, &event) ); } 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; }
/** applies a cut that is a bound change directly as bound change instead of adding it as row to the LP */ static SCIP_RETCODE sepastoreApplyBdchg( SCIP_SEPASTORE* sepastore, /**< separation storage */ BMS_BLKMEM* blkmem, /**< block memory */ SCIP_SET* set, /**< global SCIP settings */ SCIP_STAT* stat, /**< problem statistics */ SCIP_TREE* tree, /**< branch and bound tree */ SCIP_LP* lp, /**< LP data */ SCIP_BRANCHCAND* branchcand, /**< branching candidate storage */ SCIP_EVENTQUEUE* eventqueue, /**< event queue */ SCIP_ROW* cut, /**< cut with a single variable */ SCIP_Bool* cutoff /**< pointer to store whether an empty domain was created */ ) { SCIP_COL** cols; SCIP_Real* vals; SCIP_VAR* var; SCIP_Real lhs; SCIP_Real rhs; assert(sepastore != NULL); assert(!SCIProwIsModifiable(cut)); assert(SCIProwGetNNonz(cut) == 1); assert(cutoff != NULL); *cutoff = FALSE; /* get the single variable and its coefficient of the cut */ cols = SCIProwGetCols(cut); assert(cols != NULL); var = SCIPcolGetVar(cols[0]); vals = SCIProwGetVals(cut); assert(vals != NULL); assert(!SCIPsetIsZero(set, vals[0])); /* if the coefficient is nearly zero, we better ignore this cut for numerical reasons */ if( SCIPsetIsFeasZero(set, vals[0]) ) return SCIP_OKAY; /* get the left hand side of the cut and convert it to a bound */ lhs = SCIProwGetLhs(cut); if( !SCIPsetIsInfinity(set, -lhs) ) { lhs -= SCIProwGetConstant(cut); if( vals[0] > 0.0 ) { /* coefficient is positive -> lhs corresponds to lower bound */ SCIP_CALL( sepastoreApplyLb(sepastore, blkmem, set, stat, tree, lp, branchcand, eventqueue, var, lhs/vals[0], cutoff) ); } else { /* coefficient is negative -> lhs corresponds to upper bound */ SCIP_CALL( sepastoreApplyUb(sepastore, blkmem, set, stat, tree, lp, branchcand, eventqueue, var, lhs/vals[0], cutoff) ); } } /* get the right hand side of the cut and convert it to a bound */ rhs = SCIProwGetRhs(cut); if( !SCIPsetIsInfinity(set, rhs) ) { rhs -= SCIProwGetConstant(cut); if( vals[0] > 0.0 ) { /* coefficient is positive -> rhs corresponds to upper bound */ SCIP_CALL( sepastoreApplyUb(sepastore, blkmem, set, stat, tree, lp, branchcand, eventqueue, var, rhs/vals[0], cutoff) ); } else { /* coefficient is negative -> rhs corresponds to lower bound */ SCIP_CALL( sepastoreApplyLb(sepastore, blkmem, set, stat, tree, lp, branchcand, eventqueue, var, rhs/vals[0], cutoff) ); } } /* count the bound change as applied cut */ if( !sepastore->initiallp ) sepastore->ncutsapplied++; 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; }
/** execution method of primal heuristic */ static SCIP_DECL_HEUREXEC(heurExecZirounding) { /*lint --e{715}*/ SCIP_HEURDATA* heurdata; SCIP_SOL* sol; SCIP_VAR** lpcands; SCIP_VAR** zilpcands; SCIP_VAR** slackvars; SCIP_Real* upslacks; SCIP_Real* downslacks; SCIP_Real* activities; SCIP_Real* slackvarcoeffs; SCIP_Bool* rowneedsslackvar; SCIP_ROW** rows; SCIP_Real* lpcandssol; SCIP_Real* solarray; SCIP_Longint nlps; int currentlpcands; int nlpcands; int nimplfracs; int i; int c; int nslacks; int nroundings; SCIP_RETCODE retcode; SCIP_Bool improvementfound; SCIP_Bool numericalerror; assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0); assert(result != NULL); assert(SCIPhasCurrentNodeLP(scip)); *result = SCIP_DIDNOTRUN; /* do not call heuristic of node was already detected to be infeasible */ if( nodeinfeasible ) return SCIP_OKAY; /* only call heuristic if an optimal LP-solution is at hand */ if( SCIPgetLPSolstat(scip) != SCIP_LPSOLSTAT_OPTIMAL ) return SCIP_OKAY; /* only call heuristic, if the LP objective value is smaller than the cutoff bound */ if( SCIPisGE(scip, SCIPgetLPObjval(scip), SCIPgetCutoffbound(scip)) ) return SCIP_OKAY; /* get heuristic data */ heurdata = SCIPheurGetData(heur); assert(heurdata != NULL); /* Do not call heuristic if deactivation check is enabled and percentage of found solutions in relation * to number of calls falls below heurdata->stoppercentage */ if( heurdata->stopziround && SCIPheurGetNCalls(heur) >= heurdata->minstopncalls && SCIPheurGetNSolsFound(heur)/(SCIP_Real)SCIPheurGetNCalls(heur) < heurdata->stoppercentage ) return SCIP_OKAY; /* assure that heuristic has not already been called after the last LP had been solved */ nlps = SCIPgetNLPs(scip); if( nlps == heurdata->lastlp ) return SCIP_OKAY; heurdata->lastlp = nlps; /* get fractional variables */ SCIP_CALL( SCIPgetLPBranchCands(scip, &lpcands, &lpcandssol, NULL, &nlpcands, NULL, &nimplfracs) ); nlpcands = nlpcands + nimplfracs; /* make sure that there is at least one fractional variable that should be integral */ if( nlpcands == 0 ) return SCIP_OKAY; assert(nlpcands > 0); assert(lpcands != NULL); assert(lpcandssol != NULL); /* get LP rows data */ rows = SCIPgetLPRows(scip); nslacks = SCIPgetNLPRows(scip); /* cannot do anything if LP is empty */ if( nslacks == 0 ) return SCIP_OKAY; assert(rows != NULL); assert(nslacks > 0); /* get the working solution from heuristic's local data */ sol = heurdata->sol; assert(sol != NULL); *result = SCIP_DIDNOTFIND; solarray = NULL; zilpcands = NULL; retcode = SCIP_OKAY; /* copy the current LP solution to the working solution and allocate memory for local data */ SCIP_CALL( SCIPlinkLPSol(scip, sol) ); SCIP_CALL_TERMINATE(retcode, SCIPallocBufferArray(scip, &solarray, nlpcands), TERMINATE); SCIP_CALL_TERMINATE(retcode, SCIPallocBufferArray(scip, &zilpcands, nlpcands), TERMINATE); /* copy necessary data to local arrays */ BMScopyMemoryArray(solarray, lpcandssol, nlpcands); BMScopyMemoryArray(zilpcands, lpcands, nlpcands); /* allocate buffer data arrays */ SCIP_CALL_TERMINATE(retcode, SCIPallocBufferArray(scip, &slackvars, nslacks), TERMINATE); SCIP_CALL_TERMINATE(retcode, SCIPallocBufferArray(scip, &upslacks, nslacks), TERMINATE); SCIP_CALL_TERMINATE(retcode, SCIPallocBufferArray(scip, &downslacks, nslacks), TERMINATE); SCIP_CALL_TERMINATE(retcode, SCIPallocBufferArray(scip, &slackvarcoeffs, nslacks), TERMINATE); SCIP_CALL_TERMINATE(retcode, SCIPallocBufferArray(scip, &rowneedsslackvar, nslacks), TERMINATE); SCIP_CALL_TERMINATE(retcode, SCIPallocBufferArray(scip, &activities, nslacks), TERMINATE); BMSclearMemoryArray(slackvars, nslacks); BMSclearMemoryArray(slackvarcoeffs, nslacks); BMSclearMemoryArray(rowneedsslackvar, nslacks); numericalerror = FALSE; nroundings = 0; /* loop over fractional variables and involved LP rows to find all rows which require a slack variable */ for( c = 0; c < nlpcands; ++c ) { SCIP_VAR* cand; SCIP_ROW** candrows; int r; int ncandrows; cand = zilpcands[c]; assert(cand != NULL); assert(SCIPcolGetLPPos(SCIPvarGetCol(cand)) >= 0); candrows = SCIPcolGetRows(SCIPvarGetCol(cand)); ncandrows = SCIPcolGetNLPNonz(SCIPvarGetCol(cand)); assert(candrows == NULL || ncandrows > 0); for( r = 0; r < ncandrows; ++r ) { int rowpos; assert(candrows != NULL); /* to please flexelint */ assert(candrows[r] != NULL); rowpos = SCIProwGetLPPos(candrows[r]); if( rowpos >= 0 && SCIPisFeasEQ(scip, SCIProwGetLhs(candrows[r]), SCIProwGetRhs(candrows[r])) ) { rowneedsslackvar[rowpos] = TRUE; SCIPdebugMessage(" Row %s needs slack variable for variable %s\n", SCIProwGetName(candrows[r]), SCIPvarGetName(cand)); } } } /* calculate row slacks for every every row that belongs to the current LP and ensure, that the current solution * has no violated constraint -- if any constraint is violated, i.e. a slack is significantly smaller than zero, * this will cause the termination of the heuristic because Zirounding does not provide feasibility recovering */ for( i = 0; i < nslacks; ++i ) { SCIP_ROW* row; SCIP_Real lhs; SCIP_Real rhs; row = rows[i]; assert(row != NULL); lhs = SCIProwGetLhs(row); rhs = SCIProwGetRhs(row); /* get row activity */ activities[i] = SCIPgetRowActivity(scip, row); assert(SCIPisFeasLE(scip, lhs, activities[i]) && SCIPisFeasLE(scip, activities[i], rhs)); /* in special case if LHS or RHS is (-)infinity slacks have to be initialized as infinity */ if( SCIPisInfinity(scip, -lhs) ) downslacks[i] = SCIPinfinity(scip); else downslacks[i] = activities[i] - lhs; if( SCIPisInfinity(scip, rhs) ) upslacks[i] = SCIPinfinity(scip); else upslacks[i] = rhs - activities[i]; SCIPdebugMessage("lhs:%5.2f <= act:%5.2g <= rhs:%5.2g --> down: %5.2g, up:%5.2g\n", lhs, activities[i], rhs, downslacks[i], upslacks[i]); /* row is an equation. Try to find a slack variable in the row, i.e., * a continuous variable which occurs only in this row. If no such variable exists, * there is no hope for an IP-feasible solution in this round */ if( SCIPisFeasEQ(scip, lhs, rhs) && rowneedsslackvar[i] ) { /* @todo: This is only necessary for rows containing fractional variables. */ rowFindSlackVar(scip, row, &(slackvars[i]), &(slackvarcoeffs[i])); if( slackvars[i] == NULL ) { SCIPdebugMessage("No slack variable found for equation %s, terminating ZI Round heuristic\n", SCIProwGetName(row)); goto TERMINATE; } else { SCIP_Real ubslackvar; SCIP_Real lbslackvar; SCIP_Real solvalslackvar; SCIP_Real coeffslackvar; SCIP_Real ubgap; SCIP_Real lbgap; assert(SCIPvarGetType(slackvars[i]) == SCIP_VARTYPE_CONTINUOUS); solvalslackvar = SCIPgetSolVal(scip, sol, slackvars[i]); ubslackvar = SCIPvarGetUbGlobal(slackvars[i]); lbslackvar = SCIPvarGetLbGlobal(slackvars[i]); coeffslackvar = slackvarcoeffs[i]; assert(!SCIPisFeasZero(scip, coeffslackvar)); ubgap = ubslackvar - solvalslackvar; lbgap = solvalslackvar - lbslackvar; if( SCIPisFeasZero(scip, ubgap) ) ubgap = 0.0; if( SCIPisFeasZero(scip, lbgap) ) lbgap = 0.0; if( SCIPisFeasPositive(scip, coeffslackvar) ) { if( !SCIPisInfinity(scip, lbslackvar) ) upslacks[i] += coeffslackvar * lbgap; else upslacks[i] = SCIPinfinity(scip); if( !SCIPisInfinity(scip, ubslackvar) ) downslacks[i] += coeffslackvar * ubgap; else downslacks[i] = SCIPinfinity(scip); } else { if( !SCIPisInfinity(scip, ubslackvar) ) upslacks[i] -= coeffslackvar * ubgap; else upslacks[i] = SCIPinfinity(scip); if( !SCIPisInfinity(scip, lbslackvar) ) downslacks[i] -= coeffslackvar * lbgap; else downslacks[i] = SCIPinfinity(scip); } SCIPdebugMessage(" Slack variable for row %s at pos %d: %g <= %s = %g <= %g; Coeff %g, upslack = %g, downslack = %g \n", SCIProwGetName(row), SCIProwGetLPPos(row), lbslackvar, SCIPvarGetName(slackvars[i]), solvalslackvar, ubslackvar, coeffslackvar, upslacks[i], downslacks[i]); } } /* due to numerical inaccuracies, the rows might be feasible, even if the slacks are * significantly smaller than zero -> terminate */ if( SCIPisFeasLT(scip, upslacks[i], 0.0) || SCIPisFeasLT(scip, downslacks[i], 0.0) ) goto TERMINATE; } assert(nslacks == 0 || (upslacks != NULL && downslacks != NULL && activities != NULL)); /* initialize number of remaining variables and flag to enter the main loop */ currentlpcands = nlpcands; improvementfound = TRUE; /* iterate over variables as long as there are fractional variables left */ while( currentlpcands > 0 && improvementfound && (heurdata->maxroundingloops == -1 || nroundings < heurdata->maxroundingloops) ) { /*lint --e{850}*/ improvementfound = FALSE; nroundings++; SCIPdebugMessage("zirounding enters while loop for %d time with %d candidates left. \n", nroundings, currentlpcands); /* check for every remaining fractional variable if a shifting decreases ZI-value of the variable */ for( c = 0; c < currentlpcands; ++c ) { SCIP_VAR* var; SCIP_Real oldsolval; SCIP_Real upperbound; SCIP_Real lowerbound; SCIP_Real up; SCIP_Real down; SCIP_Real ziup; SCIP_Real zidown; SCIP_Real zicurrent; SCIP_Real shiftval; DIRECTION direction; /* get values from local data */ oldsolval = solarray[c]; var = zilpcands[c]; assert(!SCIPisFeasIntegral(scip, oldsolval)); assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_COLUMN); /* calculate bounds for variable and make sure that there are no numerical inconsistencies */ upperbound = SCIPinfinity(scip); lowerbound = SCIPinfinity(scip); calculateBounds(scip, var, oldsolval, &upperbound, &lowerbound, upslacks, downslacks, nslacks, &numericalerror); if( numericalerror ) goto TERMINATE; /* calculate the possible values after shifting */ up = oldsolval + upperbound; down = oldsolval - lowerbound; /* if the variable is integer or implicit binary, do not shift further than the nearest integer */ if( SCIPvarGetType(var) != SCIP_VARTYPE_BINARY) { SCIP_Real ceilx; SCIP_Real floorx; ceilx = SCIPfeasCeil(scip, oldsolval); floorx = SCIPfeasFloor(scip, oldsolval); up = MIN(up, ceilx); down = MAX(down, floorx); } /* calculate necessary values */ ziup = getZiValue(scip, up); zidown = getZiValue(scip, down); zicurrent = getZiValue(scip, oldsolval); /* calculate the shifting direction that reduces ZI-value the most, * if both directions improve ZI-value equally, take the direction which improves the objective */ if( SCIPisFeasLT(scip, zidown, zicurrent) || SCIPisFeasLT(scip, ziup, zicurrent) ) { if( SCIPisFeasEQ(scip,ziup, zidown) ) direction = SCIPisFeasGE(scip, SCIPvarGetObj(var), 0.0) ? DIRECTION_DOWN : DIRECTION_UP; else if( SCIPisFeasLT(scip, zidown, ziup) ) direction = DIRECTION_DOWN; else direction = DIRECTION_UP; /* once a possible shifting direction and value have been found, variable value is updated */ shiftval = (direction == DIRECTION_UP ? up - oldsolval : down - oldsolval); /* this improves numerical stability in some cases */ if( direction == DIRECTION_UP ) shiftval = MIN(shiftval, upperbound); else shiftval = MIN(shiftval, lowerbound); /* update the solution */ solarray[c] = direction == DIRECTION_UP ? up : down; SCIP_CALL( SCIPsetSolVal(scip, sol, var, solarray[c]) ); /* update the rows activities and slacks */ SCIP_CALL( updateSlacks(scip, sol, var, shiftval, upslacks, downslacks, activities, slackvars, slackvarcoeffs, nslacks) ); SCIPdebugMessage("zirounding update step : %d var index, oldsolval=%g, shiftval=%g\n", SCIPvarGetIndex(var), oldsolval, shiftval); /* since at least one improvement has been found, heuristic will enter main loop for another time because the improvement * might affect many LP rows and their current slacks and thus make further rounding steps possible */ improvementfound = TRUE; } /* if solution value of variable has become feasibly integral due to rounding step, * variable is put at the end of remaining candidates array so as not to be considered in future loops */ if( SCIPisFeasIntegral(scip, solarray[c]) ) { zilpcands[c] = zilpcands[currentlpcands - 1]; solarray[c] = solarray[currentlpcands - 1]; currentlpcands--; /* counter is decreased if end of candidates array has not been reached yet */ if( c < currentlpcands ) c--; } else if( nroundings == heurdata->maxroundingloops - 1 ) goto TERMINATE; } } /* in case that no candidate is left for rounding after the final main loop * the found solution has to be checked for feasibility in the original problem */ if( currentlpcands == 0 ) { SCIP_Bool stored; SCIP_CALL(SCIPtrySol(scip, sol, FALSE, FALSE, TRUE, FALSE, &stored)); if( stored ) { #ifdef SCIP_DEBUG SCIPdebugMessage("found feasible rounded solution:\n"); SCIP_CALL( SCIPprintSol(scip, sol, NULL, FALSE) ); #endif SCIPstatisticMessage(" ZI Round solution value: %g \n", SCIPgetSolOrigObj(scip, sol)); *result = SCIP_FOUNDSOL; } } /* free memory for all locally allocated data */ TERMINATE: SCIPfreeBufferArrayNull(scip, &activities); SCIPfreeBufferArrayNull(scip, &rowneedsslackvar); SCIPfreeBufferArrayNull(scip, &slackvarcoeffs); SCIPfreeBufferArrayNull(scip, &downslacks); SCIPfreeBufferArrayNull(scip, &upslacks); SCIPfreeBufferArrayNull(scip, &slackvars); SCIPfreeBufferArrayNull(scip, &zilpcands); SCIPfreeBufferArrayNull(scip, &solarray); return retcode; }
/** 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; }
/** 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(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; }