/** solving process initialization method of variable pricer (called when branch and bound process is about to begin) */ static SCIP_DECL_PRICERINITSOL(pricerInitsolStp) { SCIP_PRICERDATA* pricerdata; SCIPdebugPrintf("pricerinitsol \n"); assert(scip != NULL); assert(pricer != NULL); pricerdata = SCIPpricerGetData(pricer); assert(pricerdata != NULL); /* allocate memory */ if( !pricerdata->bigt ) { SCIP_CALL( SCIPallocMemoryArray(scip, &(pricerdata->pi), SCIPprobdataGetNEdges(scip) * SCIPprobdataGetRNTerms(scip)) ); } else { SCIP_CALL( SCIPallocMemoryArray(scip, &(pricerdata->pi), SCIPprobdataGetNEdges(scip)) ); } SCIP_CALL( SCIPallocMemoryArray(scip, &(pricerdata->mi), SCIPprobdataGetRNTerms(scip)) ); SCIP_CALL( SCIPallocMemoryArray(scip, &pricerdata->ncreatedvars, SCIPprobdataGetRNTerms(scip)) ); BMSclearMemoryArray(pricerdata->ncreatedvars, SCIPprobdataGetRNTerms(scip)); return SCIP_OKAY; }
/** reads the next line from the input file into the line buffer; skips comments; * returns whether a line could be read */ static SCIP_Bool getNextLine( BLKINPUT* blkinput /**< BLK reading data */ ) { int i; assert(blkinput != NULL); /* clear the line */ BMSclearMemoryArray(blkinput->linebuf, BLK_MAX_LINELEN); /* read next line */ blkinput->linepos = 0; blkinput->linebuf[BLK_MAX_LINELEN-2] = '\0'; if( SCIPfgets(blkinput->linebuf, sizeof(blkinput->linebuf), blkinput->file) == NULL ) return FALSE; blkinput->linenumber++; if( blkinput->linebuf[BLK_MAX_LINELEN-2] != '\0' ) { SCIPerrorMessage("Error: line %d exceeds %d characters\n", blkinput->linenumber, BLK_MAX_LINELEN-2); blkinput->haserror = TRUE; return FALSE; } blkinput->linebuf[BLK_MAX_LINELEN-1] = '\0'; blkinput->linebuf[BLK_MAX_LINELEN-2] = '\0'; /* we want to use lookahead of one char -> we need two \0 at the end */ /* skip characters after comment symbol */ for( i = 0; commentchars[i] != '\0'; ++i ) { char* commentstart; commentstart = strchr(blkinput->linebuf, commentchars[i]); if( commentstart != NULL ) { *commentstart = '\0'; *(commentstart+1) = '\0'; /* we want to use lookahead of one char -> we need two \0 at the end */ } } return TRUE; }
/** 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; }
/** branches the searching tree, branching nodes are selected in decreasing order of their apriori bound, * returns the level to which we should backtrack, or INT_MAX for continuing normally */ static int branch( TCLIQUE_GETNNODES((*getnnodes)), /**< user function to get the number of nodes */ TCLIQUE_GETWEIGHTS((*getweights)), /**< user function to get the node weights */ TCLIQUE_ISEDGE ((*isedge)), /**< user function to check for existence of an edge */ TCLIQUE_SELECTADJNODES((*selectadjnodes)), /**< user function to select adjacent edges */ TCLIQUE_GRAPH* tcliquegraph, /**< pointer to graph data structure */ TCLIQUE_NEWSOL ((*newsol)), /**< user function to call on every new solution */ TCLIQUE_DATA* tcliquedata, /**< user data to pass to user callback function */ BMS_CHKMEM* mem, /**< block memory */ CLIQUEHASH* cliquehash, /**< clique hash table */ int* buffer, /**< buffer of size nnodes */ int level, /**< level of b&b tree */ int* V, /**< non-zero weighted nodes for branching */ int nV, /**< number of non-zero weighted nodes for branching */ int* Vzero, /**< zero weighted nodes */ int nVzero, /**< number of zero weighted nodes */ NBC* gsd, /**< neighbour color information of all nodes */ TCLIQUE_Bool* iscolored, /**< coloring status of all nodes */ int* K, /**< nodes from the b&b tree */ TCLIQUE_WEIGHT weightK, /**< weight of the nodes from b&b tree */ int* maxcliquenodes, /**< pointer to store nodes of the maximum weight clique */ int* nmaxcliquenodes, /**< pointer to store number of nodes in the maximum weight clique */ TCLIQUE_WEIGHT* maxcliqueweight, /**< pointer to store weight of the maximum weight clique */ int* curcliquenodes, /**< pointer to store nodes of currenct clique */ int* ncurcliquenodes, /**< pointer to store number of nodes in current clique */ TCLIQUE_WEIGHT* curcliqueweight, /**< pointer to store weight of current clique */ int* tmpcliquenodes, /**< buffer for storing the temporary clique */ TCLIQUE_WEIGHT maxfirstnodeweight, /**< maximum weight of branching nodes in level 0; 0 if not used ** (for cliques with at least one fractional node) */ int* ntreenodes, /**< pointer to store number of nodes of b&b tree */ int maxntreenodes, /**< maximal number of nodes of b&b tree */ int backtrackfreq, /**< frequency to backtrack to first level of tree (0: no premature backtracking) */ int maxnzeroextensions, /**< maximal number of zero-valued variables extending the clique */ int fixednode, /**< node that is forced to be in the clique, or -1; must have positive weight */ TCLIQUE_STATUS* status /**< pointer to store the status of the solving call */ ) { TCLIQUE_Bool isleaf; const TCLIQUE_WEIGHT* weights; TCLIQUE_WEIGHT* apbound; TCLIQUE_WEIGHT subgraphweight; TCLIQUE_WEIGHT weightKold; TCLIQUE_WEIGHT tmpcliqueweight; int backtracklevel; int ntmpcliquenodes; int i; assert(getnnodes != NULL); assert(getweights != NULL); assert(selectadjnodes != NULL); assert(mem != NULL); assert(V != NULL); assert(gsd != NULL); assert(iscolored != NULL); assert(K != NULL); assert(maxcliqueweight != NULL); assert(curcliquenodes != NULL); assert(ncurcliquenodes != NULL); assert(curcliqueweight != NULL); assert(ntreenodes != NULL); assert(maxfirstnodeweight >= 0); assert(*ntreenodes >= 0); assert(maxntreenodes >= 0); assert(status != NULL); /* increase the number of nodes, and stop solving, if the node limit is exceeded */ (*ntreenodes)++; #ifdef TCLIQUE_DEBUG debugMessage("(level %d, treenode %d) maxclique = %d, curclique = %d [mem=%lld (%lld), cliques=%d]\n", level, *ntreenodes, *maxcliqueweight, *curcliqueweight, BMSgetChunkMemoryUsed(mem), BMSgetMemoryUsed(), cliquehash == NULL ? 0 : cliquehash->ncliques); debugMessage(" -> current branching (weight %d):", weightK); for( i = 0; i < level; ++i ) debugPrintf(" %d", K[i]); debugPrintf("\n"); debugMessage(" -> branching candidates:"); for( i = 0; i < nV; ++i ) debugPrintf(" %d", V[i]); debugPrintf("\n"); #endif if( *ntreenodes > maxntreenodes ) { *status = TCLIQUE_NODELIMIT; return TRUE; } weights = getweights(tcliquegraph); backtracklevel = INT_MAX; isleaf = TRUE; /* allocate temporary memory for a priori bounds */ ALLOC_ABORT( BMSallocMemoryArray(&apbound, nV) ); BMSclearMemoryArray(apbound, nV); /* use coloring relaxation to generate an upper bound for the current subtree and a heuristic solution */ subgraphweight = boundSubgraph(getnnodes, getweights, isedge, selectadjnodes, tcliquegraph, mem, buffer, V, nV, gsd, iscolored, apbound, tmpcliquenodes, &ntmpcliquenodes, &tmpcliqueweight); #ifndef NDEBUG /* check correctness of V and apbound arrays */ for( i = 0; i < nV; ++i ) { assert(0 <= V[i] && V[i] < getnnodes(tcliquegraph)); assert(i == 0 || V[i-1] < V[i]); assert(apbound[i] >= 0); assert((apbound[i] == 0) == (weights[V[i]] == 0)); } #endif /* check, whether the heuristic solution is better than the current subtree's solution; * if the user wanted to have a fixed variable inside the clique and we are in level 0, we first have to * fix this variable in this level (the current clique might not contain the fixed node) */ if( weightK + tmpcliqueweight > *curcliqueweight && (level > 0 || fixednode == -1) ) { /* install the newly generated clique as current clique */ for( i = 0; i < level; ++i ) curcliquenodes[i] = K[i]; for( i = 0; i < ntmpcliquenodes; ++i ) curcliquenodes[level+i] = tmpcliquenodes[i]; *ncurcliquenodes = level + ntmpcliquenodes; *curcliqueweight = weightK + tmpcliqueweight; #ifdef TCLIQUE_DEBUG debugMessage(" -> new current clique with weight %d at node %d in level %d:", *curcliqueweight, *ntreenodes, level); for( i = 0; i < *ncurcliquenodes; ++i ) debugPrintf(" %d", curcliquenodes[i]); debugPrintf("\n"); #endif } /* discard subtree, if the upper bound is not better than the weight of the currently best clique; * if only 2 nodes are left, the maximal weighted clique was already calculated in boundSubgraph() and nothing * more has to be done; * however, if the user wanted to have a fixed node and we are in the first decision level, we have to continue */ if( weightK + subgraphweight > *maxcliqueweight && (nV > 2 || (fixednode >= 0 && level == 0)) ) { int* Vcurrent; int nVcurrent; int nValive; int branchingnode; assert(nV > 0); /* process current subtree */ level++; /* set up data structures */ ALLOC_ABORT( BMSallocMemoryArray(&Vcurrent, nV-1) ); nValive = nV; weightKold = weightK; debugMessage("============================ branching level %d ===============================\n", level); /* branch on the nodes of V by decreasing order of their apriori bound */ while( backtracklevel >= level && nValive > 0 ) { int branchidx; /* check if we meet the backtracking frequency - in this case abort the search until we have reached first level */ if( level > 1 && backtrackfreq > 0 && (*ntreenodes) % backtrackfreq == 0 ) { backtracklevel = 1; break; } /* get next branching node */ if( level == 1 && fixednode >= 0 ) { /* select the fixed node as first "branching" candidate */ for( branchidx = 0; branchidx < nValive && V[branchidx] != fixednode; branchidx++ ) {} assert(branchidx < nValive); assert(V[branchidx] == fixednode); } else if( level == 1 && maxfirstnodeweight > 0 ) branchidx = getMaxApBoundIndexNotMaxWeight(V, nValive, apbound, weights, maxfirstnodeweight); else branchidx = getMaxApBoundIndex(nValive, apbound); if( branchidx < 0 ) break; assert(0 <= branchidx && branchidx < nValive && nValive <= nV); assert(apbound[branchidx] > 0); assert(weights[V[branchidx]] > 0); /* test a priori bound */ if( (weightKold + apbound[branchidx]) <= *maxcliqueweight ) break; debugMessage("%d. branching in level %d (treenode %d): bidx=%d, node %d, weight %d, upperbound: %d+%d = %d, maxclique=%d\n", nV-nValive+1, level, *ntreenodes, branchidx, V[branchidx], weights[V[branchidx]], weightKold, apbound[branchidx], weightKold + apbound[branchidx], *maxcliqueweight); /* because we branch on this node, the node is no leaf in the tree */ isleaf = FALSE; /* update the set of nodes from the b&b tree * K = K & {branchingnode} */ branchingnode = V[branchidx]; K[level-1] = branchingnode; weightK = weightKold + weights[branchingnode]; /* update the set of nodes for branching * V = V \ {branchingnode} */ nValive--; for( i = branchidx; i < nValive; ++i ) { V[i] = V[i+1]; apbound[i] = apbound[i+1]; } /* set the nodes for the next level of b&b tree * Vcurrent = nodes of V, that are adjacent to branchingnode */ nVcurrent = selectadjnodes(tcliquegraph, branchingnode, V, nValive, Vcurrent); /* process the selected subtree */ backtracklevel = branch(getnnodes, getweights, isedge, selectadjnodes, tcliquegraph, newsol, tcliquedata, mem, cliquehash, buffer, level, Vcurrent, nVcurrent, Vzero, nVzero, gsd, iscolored, K, weightK, maxcliquenodes, nmaxcliquenodes, maxcliqueweight, curcliquenodes, ncurcliquenodes, curcliqueweight, tmpcliquenodes, maxfirstnodeweight, ntreenodes, maxntreenodes, backtrackfreq, maxnzeroextensions, -1, status); /* if all other candidates stayed in the candidate list, the current branching was optimal and * there is no need to try the remaining ones */ if( nVcurrent == nValive ) { debugMessage("branching on node %d was optimal - ignoring remaining candidates\n", branchingnode); nValive = 0; } /* if we had a fixed node, ignore all other nodes */ if( fixednode >= 0 ) nValive = 0; } debugMessage("========================== branching level %d end =============================\n\n", level); /* free data structures */ BMSfreeMemoryArray(&Vcurrent); } /* check, whether any branchings have been applied, or if this node is a leaf of the branching tree */ if( isleaf ) { /* the current clique is the best clique found on the path to this leaf * -> check, whether it is an improvement to the currently best clique */ if( *curcliqueweight > *maxcliqueweight ) { TCLIQUE_Bool stopsolving; debugMessage("found clique of weight %d at node %d in level %d\n", *curcliqueweight, *ntreenodes, level); newSolution(selectadjnodes, tcliquegraph, newsol, tcliquedata, cliquehash, buffer, Vzero, nVzero, maxnzeroextensions, curcliquenodes, *ncurcliquenodes, *curcliqueweight, maxcliquenodes, nmaxcliquenodes, maxcliqueweight, &stopsolving); if( stopsolving ) { debugMessage(" -> solving terminated by callback method\n"); backtracklevel = 0; } } /* discard the current clique */ *ncurcliquenodes = 0; *curcliqueweight = 0; } #ifdef TCLIQUE_DEBUG if( level > backtracklevel ) { debugMessage("premature backtracking after %d nodes - level %d\n", *ntreenodes, level); } #endif /* free data structures */ BMSfreeMemoryArray(&apbound); return backtracklevel; }
/** execution method of primal heuristic */ static SCIP_DECL_HEUREXEC(heurExecObjpscostdiving) /*lint --e{715}*/ { /*lint --e{715}*/ SCIP_HEURDATA* heurdata; SCIP_LPSOLSTAT lpsolstat; SCIP_VAR* var; SCIP_VAR** lpcands; SCIP_Real* lpcandssol; SCIP_Real* lpcandsfrac; SCIP_Real primsol; SCIP_Real frac; SCIP_Real pscostquot; SCIP_Real bestpscostquot; SCIP_Real oldobj; SCIP_Real newobj; SCIP_Real objscale; SCIP_Bool bestcandmayrounddown; SCIP_Bool bestcandmayroundup; SCIP_Bool bestcandroundup; SCIP_Bool mayrounddown; SCIP_Bool mayroundup; SCIP_Bool roundup; SCIP_Bool lperror; SCIP_Longint ncalls; SCIP_Longint nsolsfound; SCIP_Longint nlpiterations; SCIP_Longint maxnlpiterations; int* roundings; int nvars; int varidx; int nlpcands; int startnlpcands; int depth; int maxdepth; int maxdivedepth; int divedepth; int bestcand; int c; assert(heur != NULL); assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0); assert(scip != NULL); assert(result != NULL); assert(SCIPhasCurrentNodeLP(scip)); *result = SCIP_DELAYED; /* 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; /* only call heuristic, if the LP solution is basic (which allows fast resolve in diving) */ if( !SCIPisLPSolBasic(scip) ) return SCIP_OKAY; /* don't dive two times at the same node */ if( SCIPgetLastDivenode(scip) == SCIPgetNNodes(scip) && SCIPgetDepth(scip) > 0 ) return SCIP_OKAY; *result = SCIP_DIDNOTRUN; /* get heuristic's data */ heurdata = SCIPheurGetData(heur); assert(heurdata != NULL); /* only apply heuristic, if only a few solutions have been found */ if( heurdata->maxsols >= 0 && SCIPgetNSolsFound(scip) >= heurdata->maxsols ) return SCIP_OKAY; /* only try to dive, if we are in the correct part of the tree, given by minreldepth and maxreldepth */ depth = SCIPgetDepth(scip); maxdepth = SCIPgetMaxDepth(scip); maxdepth = MAX(maxdepth, 30); if( depth < heurdata->minreldepth*maxdepth || depth > heurdata->maxreldepth*maxdepth ) return SCIP_OKAY; /* calculate the maximal number of LP iterations until heuristic is aborted */ nlpiterations = SCIPgetNNodeLPIterations(scip); ncalls = SCIPheurGetNCalls(heur); nsolsfound = 10*SCIPheurGetNBestSolsFound(heur) + heurdata->nsuccess; maxnlpiterations = (SCIP_Longint)((1.0 + 10.0*(nsolsfound+1.0)/(ncalls+1.0)) * heurdata->maxlpiterquot * nlpiterations); maxnlpiterations += heurdata->maxlpiterofs; /* don't try to dive, if we took too many LP iterations during diving */ if( heurdata->nlpiterations >= maxnlpiterations ) return SCIP_OKAY; /* allow at least a certain number of LP iterations in this dive */ maxnlpiterations = MAX(maxnlpiterations, heurdata->nlpiterations + MINLPITER); /* get fractional variables that should be integral */ SCIP_CALL( SCIPgetLPBranchCands(scip, &lpcands, &lpcandssol, &lpcandsfrac, &nlpcands, NULL, NULL) ); /* don't try to dive, if there are no fractional variables */ if( nlpcands == 0 ) return SCIP_OKAY; /* calculate the maximal diving depth */ nvars = SCIPgetNBinVars(scip) + SCIPgetNIntVars(scip); if( SCIPgetNSolsFound(scip) == 0 ) maxdivedepth = (int)(heurdata->depthfacnosol * nvars); else maxdivedepth = (int)(heurdata->depthfac * nvars); maxdivedepth = MIN(maxdivedepth, 10*maxdepth); *result = SCIP_DIDNOTFIND; /* get temporary memory for remembering the current soft roundings */ SCIP_CALL( SCIPallocBufferArray(scip, &roundings, nvars) ); BMSclearMemoryArray(roundings, nvars); /* start diving */ SCIP_CALL( SCIPstartDive(scip) ); SCIPdebugMessage("(node %"SCIP_LONGINT_FORMAT") executing objpscostdiving heuristic: depth=%d, %d fractionals, dualbound=%g, maxnlpiterations=%"SCIP_LONGINT_FORMAT", maxdivedepth=%d\n", SCIPgetNNodes(scip), SCIPgetDepth(scip), nlpcands, SCIPgetDualbound(scip), maxnlpiterations, maxdivedepth); /* dive as long we are in the given diving depth and iteration limits and fractional variables exist, but * - if the last objective change was in a direction, that corresponds to a feasible rounding, we continue in any case * - if possible, we dive at least with the depth 10 * - if the number of fractional variables decreased at least with 1 variable per 2 dive depths, we continue diving */ lperror = FALSE; lpsolstat = SCIP_LPSOLSTAT_OPTIMAL; divedepth = 0; bestcandmayrounddown = FALSE; bestcandmayroundup = FALSE; startnlpcands = nlpcands; while( !lperror && lpsolstat == SCIP_LPSOLSTAT_OPTIMAL && nlpcands > 0 && (divedepth < 10 || nlpcands <= startnlpcands - divedepth/2 || (divedepth < maxdivedepth && nlpcands <= startnlpcands - divedepth/10 && heurdata->nlpiterations < maxnlpiterations)) && !SCIPisStopped(scip) ) { SCIP_RETCODE retcode; divedepth++; /* choose variable for objective change: * - prefer variables that may not be rounded without destroying LP feasibility: * - of these variables, change objective value of variable with largest rel. difference of pseudo cost values * - if all remaining fractional variables may be rounded without destroying LP feasibility: * - change objective value of variable with largest rel. difference of pseudo cost values */ bestcand = -1; bestpscostquot = -1.0; bestcandmayrounddown = TRUE; bestcandmayroundup = TRUE; bestcandroundup = FALSE; for( c = 0; c < nlpcands; ++c ) { var = lpcands[c]; mayrounddown = SCIPvarMayRoundDown(var); mayroundup = SCIPvarMayRoundUp(var); primsol = lpcandssol[c]; frac = lpcandsfrac[c]; if( mayrounddown || mayroundup ) { /* the candidate may be rounded: choose this candidate only, if the best candidate may also be rounded */ if( bestcandmayrounddown || bestcandmayroundup ) { /* choose rounding direction: * - if variable may be rounded in both directions, round corresponding to the pseudo cost values * - otherwise, round in the infeasible direction, because feasible direction is tried by rounding * the current fractional solution */ roundup = FALSE; if( mayrounddown && mayroundup ) calcPscostQuot(scip, var, primsol, frac, 0, &pscostquot, &roundup); else if( mayrounddown ) calcPscostQuot(scip, var, primsol, frac, +1, &pscostquot, &roundup); else calcPscostQuot(scip, var, primsol, frac, -1, &pscostquot, &roundup); /* prefer variables, that have already been soft rounded but failed to get integral */ varidx = SCIPvarGetProbindex(var); assert(0 <= varidx && varidx < nvars); if( roundings[varidx] != 0 ) pscostquot *= 1000.0; /* check, if candidate is new best candidate */ if( pscostquot > bestpscostquot ) { bestcand = c; bestpscostquot = pscostquot; bestcandmayrounddown = mayrounddown; bestcandmayroundup = mayroundup; bestcandroundup = roundup; } } } else { /* the candidate may not be rounded: calculate pseudo cost quotient and preferred direction */ calcPscostQuot(scip, var, primsol, frac, 0, &pscostquot, &roundup); /* prefer variables, that have already been soft rounded but failed to get integral */ varidx = SCIPvarGetProbindex(var); assert(0 <= varidx && varidx < nvars); if( roundings[varidx] != 0 ) pscostquot *= 1000.0; /* check, if candidate is new best candidate: prefer unroundable candidates in any case */ if( bestcandmayrounddown || bestcandmayroundup || pscostquot > bestpscostquot ) { bestcand = c; bestpscostquot = pscostquot; bestcandmayrounddown = FALSE; bestcandmayroundup = FALSE; bestcandroundup = roundup; } } } assert(bestcand != -1); /* if all candidates are roundable, try to round the solution */ if( bestcandmayrounddown || bestcandmayroundup ) { SCIP_Bool success; /* create solution from diving LP and try to round it */ SCIP_CALL( SCIPlinkLPSol(scip, heurdata->sol) ); SCIP_CALL( SCIProundSol(scip, heurdata->sol, &success) ); if( success ) { SCIPdebugMessage("objpscostdiving found roundable primal solution: obj=%g\n", SCIPgetSolOrigObj(scip, heurdata->sol)); /* try to add solution to SCIP */ SCIP_CALL( SCIPtrySol(scip, heurdata->sol, FALSE, FALSE, FALSE, FALSE, &success) ); /* check, if solution was feasible and good enough */ if( success ) { SCIPdebugMessage(" -> solution was feasible and good enough\n"); *result = SCIP_FOUNDSOL; } } } var = lpcands[bestcand]; /* check, if the best candidate was already subject to soft rounding */ varidx = SCIPvarGetProbindex(var); assert(0 <= varidx && varidx < nvars); if( roundings[varidx] == +1 ) { /* variable was already soft rounded upwards: hard round it downwards */ SCIP_CALL( SCIPchgVarUbDive(scip, var, SCIPfeasFloor(scip, lpcandssol[bestcand])) ); SCIPdebugMessage(" dive %d/%d: var <%s>, round=%u/%u, sol=%g, was already soft rounded upwards -> bounds=[%g,%g]\n", divedepth, maxdivedepth, SCIPvarGetName(var), bestcandmayrounddown, bestcandmayroundup, lpcandssol[bestcand], SCIPgetVarLbDive(scip, var), SCIPgetVarUbDive(scip, var)); } else if( roundings[varidx] == -1 ) { /* variable was already soft rounded downwards: hard round it upwards */ SCIP_CALL( SCIPchgVarLbDive(scip, var, SCIPfeasCeil(scip, lpcandssol[bestcand])) ); SCIPdebugMessage(" dive %d/%d: var <%s>, round=%u/%u, sol=%g, was already soft rounded downwards -> bounds=[%g,%g]\n", divedepth, maxdivedepth, SCIPvarGetName(var), bestcandmayrounddown, bestcandmayroundup, lpcandssol[bestcand], SCIPgetVarLbDive(scip, var), SCIPgetVarUbDive(scip, var)); } else { assert(roundings[varidx] == 0); /* apply soft rounding of best candidate via a change in the objective value */ objscale = divedepth * 1000.0; oldobj = SCIPgetVarObjDive(scip, var); if( bestcandroundup ) { /* soft round variable up: make objective value (more) negative */ if( oldobj < 0.0 ) newobj = objscale * oldobj; else newobj = -objscale * oldobj; newobj = MIN(newobj, -objscale); /* remember, that this variable was soft rounded upwards */ roundings[varidx] = +1; } else { /* soft round variable down: make objective value (more) positive */ if( oldobj > 0.0 ) newobj = objscale * oldobj; else newobj = -objscale * oldobj; newobj = MAX(newobj, objscale); /* remember, that this variable was soft rounded downwards */ roundings[varidx] = -1; } SCIP_CALL( SCIPchgVarObjDive(scip, var, newobj) ); SCIPdebugMessage(" dive %d/%d, LP iter %"SCIP_LONGINT_FORMAT"/%"SCIP_LONGINT_FORMAT": var <%s>, round=%u/%u, sol=%g, bounds=[%g,%g], obj=%g, newobj=%g\n", divedepth, maxdivedepth, heurdata->nlpiterations, maxnlpiterations, SCIPvarGetName(var), bestcandmayrounddown, bestcandmayroundup, lpcandssol[bestcand], SCIPgetVarLbDive(scip, var), SCIPgetVarUbDive(scip, var), oldobj, newobj); } /* resolve the diving LP */ nlpiterations = SCIPgetNLPIterations(scip); retcode = SCIPsolveDiveLP(scip, MAX((int)(maxnlpiterations - heurdata->nlpiterations), MINLPITER), &lperror, NULL); lpsolstat = SCIPgetLPSolstat(scip); /* 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. */ if( retcode != SCIP_OKAY ) { #ifndef NDEBUG if( lpsolstat != SCIP_LPSOLSTAT_UNBOUNDEDRAY ) { SCIP_CALL( retcode ); } #endif SCIPwarningMessage(scip, "Error while solving LP in Objpscostdiving heuristic; LP solve terminated with code <%d>\n", retcode); SCIPwarningMessage(scip, "This does not affect the remaining solution procedure --> continue\n"); } if( lperror ) break; /* update iteration count */ heurdata->nlpiterations += SCIPgetNLPIterations(scip) - nlpiterations; /* get LP solution status and fractional variables, that should be integral */ if( lpsolstat == SCIP_LPSOLSTAT_OPTIMAL ) { /* get new fractional variables */ SCIP_CALL( SCIPgetLPBranchCands(scip, &lpcands, &lpcandssol, &lpcandsfrac, &nlpcands, NULL, NULL) ); } SCIPdebugMessage(" -> lpsolstat=%d, nfrac=%d\n", lpsolstat, nlpcands); } /* check if a solution has been found */ if( nlpcands == 0 && !lperror && lpsolstat == SCIP_LPSOLSTAT_OPTIMAL ) { SCIP_Bool success; /* create solution from diving LP */ SCIP_CALL( SCIPlinkLPSol(scip, heurdata->sol) ); SCIPdebugMessage("objpscostdiving found primal solution: obj=%g\n", SCIPgetSolOrigObj(scip, heurdata->sol)); /* try to add solution to SCIP */ SCIP_CALL( SCIPtrySol(scip, heurdata->sol, FALSE, FALSE, FALSE, FALSE, &success) ); /* check, if solution was feasible and good enough */ if( success ) { SCIPdebugMessage(" -> solution was feasible and good enough\n"); *result = SCIP_FOUNDSOL; } } /* end diving */ SCIP_CALL( SCIPendDive(scip) ); if( *result == SCIP_FOUNDSOL ) heurdata->nsuccess++; /* free temporary memory for remembering the current soft roundings */ SCIPfreeBufferArray(scip, &roundings); SCIPdebugMessage("objpscostdiving heuristic finished\n"); 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; }
/** reads an BLK file */ static SCIP_RETCODE readBLKFile( SCIP* scip, /**< SCIP data structure */ SCIP_READER* reader, /**< reader data structure */ BLKINPUT* blkinput, /**< BLK reading data */ const char* filename /**< name of the input file */ ) { DEC_DECOMP *decdecomp; int i; int nconss; int nblocksread; int nvars; SCIP_READERDATA* readerdata; SCIP_CONS** conss; nblocksread = FALSE; assert(scip != NULL); assert(reader != NULL); assert(blkinput != NULL); if( SCIPgetStage(scip) < SCIP_STAGE_TRANSFORMED ) SCIP_CALL( SCIPtransformProb(scip) ); readerdata = SCIPreaderGetData(reader); assert(readerdata != NULL); readerdata->nlinkingcons = SCIPgetNConss(scip); readerdata->nlinkingvars = 0; nvars = SCIPgetNVars(scip); conss = SCIPgetConss(scip); nconss = SCIPgetNConss(scip); /* alloc: var -> block mapping */ SCIP_CALL( SCIPallocMemoryArray(scip, &readerdata->varstoblock, nvars) ); for( i = 0; i < nvars; i ++ ) { readerdata->varstoblock[i] = NOVALUE; } /* alloc: linkingvar -> blocks mapping */ SCIP_CALL( SCIPallocMemoryArray(scip, &readerdata->linkingvarsblocks, nvars) ); SCIP_CALL( SCIPallocMemoryArray(scip, &readerdata->nlinkingvarsblocks, nvars) ); BMSclearMemoryArray(readerdata->linkingvarsblocks, nvars); BMSclearMemoryArray(readerdata->nlinkingvarsblocks, nvars); /* cons -> block mapping */ SCIP_CALL( SCIPhashmapCreate(&readerdata->constoblock, SCIPblkmem(scip), nconss) ); for( i = 0; i < SCIPgetNConss(scip); i ++ ) { SCIP_CALL( SCIPhashmapInsert(readerdata->constoblock, conss[i], (void*)(size_t) NOVALUE) ); } /* open file */ blkinput->file = SCIPfopen(filename, "r"); if( blkinput->file == NULL ) { SCIPerrorMessage("cannot open file <%s> for reading\n", filename); SCIPprintSysError(filename); return SCIP_NOFILE; } /* parse the file */ blkinput->section = BLK_START; while( blkinput->section != BLK_END && !hasError(blkinput) ) { switch( blkinput->section ) { case BLK_START: SCIP_CALL( readStart(scip, blkinput) ); break; case BLK_PRESOLVED: SCIP_CALL( readPresolved(scip, blkinput) ); if( blkinput->presolved && SCIPgetStage(scip) < SCIP_STAGE_PRESOLVED ) { assert(blkinput->haspresolvesection); /** @bug GCG should be able to presolve the problem first */ SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "decomposition belongs to the presolved problem, please presolve the problem first.\n"); goto TERMINATE; } break; case BLK_NBLOCKS: SCIP_CALL( readNBlocks(scip, blkinput) ); if( blkinput->haspresolvesection && !blkinput->presolved && SCIPgetStage(scip) >= SCIP_STAGE_PRESOLVED ) { SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "decomposition belongs to the unpresolved problem, please re-read the problem and read the decomposition without presolving.\n"); goto TERMINATE; } if( !blkinput->haspresolvesection ) { SCIPwarningMessage(scip, "decomposition has no presolve section at beginning. The behaviour is undefined. See the FAQ for further information.\n"); } break; case BLK_BLOCK: if( nblocksread == FALSE ) { /* alloc n vars per block */ SCIP_CALL( SCIPallocMemoryArray(scip, &readerdata->nblockvars, blkinput->nblocks) ); SCIP_CALL( SCIPallocMemoryArray(scip, &readerdata->nblockcons, blkinput->nblocks) ); SCIP_CALL( SCIPallocMemoryArray(scip, &readerdata->blockcons, blkinput->nblocks) ); for( i = 0; i < blkinput->nblocks; ++i ) { readerdata->nblockvars[i] = 0; readerdata->nblockcons[i] = 0; SCIP_CALL( SCIPallocMemoryArray(scip, &(readerdata->blockcons[i]), nconss) ); /*lint !e866*/ } nblocksread = TRUE; } SCIP_CALL( readBlock(scip, blkinput, readerdata) ); break; case BLK_MASTERCONSS: SCIP_CALL( readMasterconss(scip, blkinput, readerdata) ); break; case BLK_END: /* this is already handled in the while() loop */ default: SCIPerrorMessage("invalid BLK file section <%d>\n", blkinput->section); return SCIP_INVALIDDATA; } } SCIP_CALL( DECdecompCreate(scip, &decdecomp) ); /* fill decomp */ SCIP_CALL( fillDecompStruct(scip, blkinput, decdecomp, readerdata) ); /* add decomp to cons_decomp */ SCIP_CALL( SCIPconshdlrDecompAddDecdecomp(scip, decdecomp) ); for( i = 0; i < nvars; ++i ) { assert(readerdata->linkingvarsblocks[i] != NULL || readerdata->nlinkingvarsblocks[i] == 0); if( readerdata->nlinkingvarsblocks[i] > 0 ) { SCIPfreeMemoryArray(scip, &readerdata->linkingvarsblocks[i]); } } TERMINATE: if( nblocksread ) { for( i = blkinput->nblocks - 1; i >= 0; --i ) { SCIPfreeMemoryArray(scip, &(readerdata->blockcons[i])); } SCIPfreeMemoryArray(scip, &readerdata->blockcons); SCIPfreeMemoryArray(scip, &readerdata->nblockcons); SCIPfreeMemoryArray(scip, &readerdata->nblockvars); } SCIPhashmapFree(&readerdata->constoblock); SCIPfreeMemoryArray(scip, &readerdata->nlinkingvarsblocks); SCIPfreeMemoryArray(scip, &readerdata->linkingvarsblocks); SCIPfreeMemoryArray(scip, &readerdata->varstoblock); /* close file */ SCIPfclose(blkinput->file); return SCIP_OKAY; }
/** get next input line; this are all characters until the next semicolon */ static SCIP_RETCODE getInputString( SCIP* scip, /**< SCIP data structure */ CIPINPUT* cipinput /**< CIP parsing data */ ) { char* endline; char* endcharacter; char* windowsendlinechar; assert(cipinput != NULL); /* read next line */ cipinput->endfile = (SCIPfgets(cipinput->strbuf, cipinput->len, cipinput->file) == NULL); if( cipinput->endfile ) { /* clear the line for safety reason */ BMSclearMemoryArray(cipinput->strbuf, cipinput->len); return SCIP_OKAY; } cipinput->linenumber++; endline = strchr(cipinput->strbuf, '\n'); endcharacter = strchr(cipinput->strbuf, ';'); while( endline == NULL || (endcharacter == NULL && cipinput->section == CIP_CONSTRAINTS && strncmp(cipinput->strbuf, "END", 3) != 0 ) ) { int pos; /* we refill the buffer from the '\n' character */ if( endline == NULL ) pos = cipinput->len - 1; else pos = (int) (endline - cipinput->strbuf); /* don't erase the '\n' from all buffers for constraints */ if( endline != NULL && cipinput->section == CIP_CONSTRAINTS ) pos++; /* if necessary reallocate memory */ if( pos + cipinput->readingsize >= cipinput->len ) { cipinput->len = SCIPcalcMemGrowSize(scip, pos + cipinput->readingsize); SCIP_CALL( SCIPreallocBufferArray(scip, &(cipinput->strbuf), cipinput->len) ); } /* read next line */ cipinput->endfile = (SCIPfgets(&(cipinput->strbuf[pos]), cipinput->len - pos, cipinput->file) == NULL); if( cipinput->endfile ) { /* clear the line for safety reason */ BMSclearMemoryArray(cipinput->strbuf, cipinput->len); return SCIP_OKAY; } cipinput->linenumber++; endline = strrchr(cipinput->strbuf, '\n'); endcharacter = strchr(cipinput->strbuf, ';'); } assert(endline != NULL); /*SCIPdebugMessage("read line: %s\n", cipinput->strbuf);*/ /* check for windows "carriage return" endline character */ windowsendlinechar = strrchr(cipinput->strbuf, '\r'); if( windowsendlinechar != NULL && windowsendlinechar + 1 == endline ) --endline; else /* if the assert should not hold we found a windows "carriage return" which was not at the end of the line */ assert(windowsendlinechar == NULL); if( cipinput->section == CIP_CONSTRAINTS && endcharacter != NULL && endline - endcharacter != 1 ) { SCIPerrorMessage("Constraint line has to end with ';\\n' (line: %d).\n", cipinput->linenumber); cipinput->haserror = TRUE; return SCIP_OKAY; /* return error at hightest level */ } *endline = '\0'; return SCIP_OKAY; }
/** execution method of presolver */ static SCIP_DECL_PRESOLEXEC(presolExecDualagg) { /*lint --e{715}*/ SCIPMILPMATRIX* matrix; SCIP_Bool initialized; SCIP_Bool complete; assert(result != NULL); *result = SCIP_DIDNOTRUN; if( (SCIPgetStage(scip) != SCIP_STAGE_PRESOLVING) || SCIPinProbing(scip) || SCIPisNLPEnabled(scip) ) return SCIP_OKAY; if( SCIPisStopped(scip) || SCIPgetNActivePricers(scip) > 0 ) return SCIP_OKAY; if( SCIPgetNBinVars(scip) == 0 ) return SCIP_OKAY; if( !SCIPallowDualReds(scip) ) return SCIP_OKAY; *result = SCIP_DIDNOTFIND; matrix = NULL; SCIP_CALL( SCIPmatrixCreate(scip, &matrix, &initialized, &complete) ); /* we only work on pure MIPs currently */ if( initialized && complete ) { AGGRTYPE* aggtypes; SCIP_VAR** binvars; int nvaragg; int ncols; ncols = SCIPmatrixGetNColumns(matrix); nvaragg = 0; SCIP_CALL( SCIPallocBufferArray(scip, &aggtypes, ncols) ); BMSclearMemoryArray(aggtypes, ncols); SCIP_CALL( SCIPallocBufferArray(scip, &binvars, ncols) ); SCIPdebug( BMSclearMemoryArray(binvars, ncols) ); /* search for aggregations */ SCIP_CALL( findUplockAggregations(scip, matrix, &nvaragg, aggtypes, binvars) ); SCIP_CALL( findDownlockAggregations(scip, matrix, &nvaragg, aggtypes, binvars) ); /* apply aggregations, if we found any */ if( nvaragg > 0 ) { int v; for( v = 0; v < ncols; v++ ) { if( aggtypes[v] != NOAGG ) { SCIP_Bool infeasible; SCIP_Bool redundant; SCIP_Bool aggregated; SCIP_Real ub; SCIP_Real lb; ub = SCIPmatrixGetColUb(matrix, v); lb = SCIPmatrixGetColLb(matrix, v); /* aggregate variable */ assert(binvars[v] != NULL); if( aggtypes[v] == BIN0UBOUND ) { SCIP_CALL( SCIPaggregateVars(scip, SCIPmatrixGetVar(matrix, v), binvars[v], 1.0, ub-lb, ub, &infeasible, &redundant, &aggregated) ); } else { assert(aggtypes[v] == BIN0LBOUND); SCIP_CALL( SCIPaggregateVars(scip, SCIPmatrixGetVar(matrix, v), binvars[v], 1.0, lb-ub, lb, &infeasible, &redundant, &aggregated) ); } /* infeasible aggregation */ if( infeasible ) { SCIPdebugMessage(" -> infeasible aggregation\n"); *result = SCIP_CUTOFF; return SCIP_OKAY; } if( aggregated ) (*naggrvars)++; } } /* set result pointer */ if( (*naggrvars) > 0 ) *result = SCIP_SUCCESS; } SCIPfreeBufferArray(scip, &binvars); SCIPfreeBufferArray(scip, &aggtypes); } SCIPmatrixFree(scip, &matrix); return SCIP_OKAY; }
/** colors the positive weighted nodes of a given set of nodes V with the lowest possible number of colors and * finds a clique in the graph induced by V, an upper bound and an apriori bound for further branching steps */ TCLIQUE_WEIGHT tcliqueColoring( TCLIQUE_GETNNODES((*getnnodes)), /**< user function to get the number of nodes */ TCLIQUE_GETWEIGHTS((*getweights)), /**< user function to get the node weights */ TCLIQUE_SELECTADJNODES((*selectadjnodes)), /**< user function to select adjacent edges */ TCLIQUE_GRAPH* tcliquegraph, /**< pointer to graph data structure */ BMS_CHKMEM* mem, /**< block memory */ int* buffer, /**< buffer of size nnodes */ int* V, /**< non-zero weighted nodes for branching */ int nV, /**< number of non-zero weighted nodes for branching */ NBC* gsd, /**< neighbor color information of all nodes */ TCLIQUE_Bool* iscolored, /**< coloring status of all nodes */ TCLIQUE_WEIGHT* apbound, /**< pointer to store apriori bound of nodes for branching */ int* clique, /**< buffer for storing the clique */ int* nclique, /**< pointer to store number of nodes in the clique */ TCLIQUE_WEIGHT* weightclique /**< pointer to store the weight of the clique */ ) { const TCLIQUE_WEIGHT* weights; TCLIQUE_WEIGHT maxsatdegree; TCLIQUE_WEIGHT range; TCLIQUE_Bool growclique; int node; int nodeVindex; int i; int j; LIST_ITV* colorinterval; LIST_ITV nwcitv; LIST_ITV* pnc; LIST_ITV* lcitv; LIST_ITV* item; LIST_ITV* tmpitem; int* workclique; int* currentclique; int ncurrentclique; int weightcurrentclique; int* Vadj; int nVadj; int adjidx; assert(getnnodes != NULL); assert(getweights != NULL); assert(selectadjnodes != NULL); assert(buffer != NULL); assert(V != NULL); assert(nV > 0); assert(clique != NULL); assert(nclique != NULL); assert(weightclique != NULL); assert(gsd != NULL); assert(iscolored != NULL); weights = getweights(tcliquegraph); assert(weights != NULL); /* initialize maximum weight clique found so far */ growclique = TRUE; *nclique = 0; *weightclique = 0; /* get node of V with maximum weight */ nodeVindex = getMaxWeightIndex(getnnodes, getweights, tcliquegraph, V, nV); node = V[nodeVindex]; assert(0 <= node && node < getnnodes(tcliquegraph)); range = weights[node]; assert(range > 0); /* set up data structures for coloring */ BMSclearMemoryArray(iscolored, nV); /* new-memory */ BMSclearMemoryArray(gsd, nV); /* new-memory */ iscolored[nodeVindex] = TRUE; /* color the first node */ debugMessage("---------------coloring-----------------\n"); debugMessage("1. node choosen: vindex=%d, vertex=%d, satdeg=%d, range=%d)\n", nodeVindex, node, gsd[nodeVindex].satdeg, range); /* set apriori bound: apbound(v_i) = satdeg(v_i) + weight(v_i) */ apbound[nodeVindex] = range; assert(apbound[nodeVindex] > 0); /* update maximum saturation degree: maxsatdeg = max { satdeg(v_i) + weight(v_i) | v_i in V } */ maxsatdegree = range; debugMessage("-> updated neighbors:\n"); /* set neighbor color of the adjacent nodes of node */ Vadj = buffer; nVadj = selectadjnodes(tcliquegraph, node, V, nV, Vadj); for( i = 0, adjidx = 0; i < nV && adjidx < nVadj; ++i ) { assert(V[i] <= Vadj[adjidx]); /* Vadj is a subset of V */ if( V[i] == Vadj[adjidx] ) { /* node is adjacent to itself, but we do not need to color it again */ if( i == nodeVindex ) { /* go to the next node in Vadj */ adjidx++; continue; } debugMessage(" nodeVindex=%d, node=%d, weight=%d, satdegold=%d -> ", i, V[i], weights[V[i]], gsd[i].satdeg); /* sets satdeg for adjacent node */ gsd[i].satdeg = range; /* creates new color interval [1,range] */ ALLOC_ABORT( BMSallocChunkMemory(mem, &colorinterval) ); colorinterval->next = NULL; colorinterval->itv.inf = 1; colorinterval->itv.sup = range; /* colorinterval is the first added element of the list of neighborcolors of the adjacent node */ gsd[i].lcitv = colorinterval; /* go to the next node in Vadj */ adjidx++; debugPrintf("satdegnew=%d, nbc=[%d,%d]\n", gsd[i].satdeg, gsd[i].lcitv->itv.inf, gsd[i].lcitv->itv.sup); } } /* set up data structures for the current clique */ ALLOC_ABORT( BMSallocMemoryArray(¤tclique, nV) ); workclique = clique; /* add node to the current clique */ currentclique[0] = node; ncurrentclique = 1; weightcurrentclique = range; /* color all other nodes of V */ for( i = 0 ; i < nV-1; i++ ) { assert((workclique == clique) != (currentclique == clique)); /* selects the next uncolored node to color */ nodeVindex = getMaxSatdegIndex(V, nV, gsd, iscolored, weights); if( nodeVindex == -1 ) /* no uncolored nodes left */ break; node = V[nodeVindex]; assert(0 <= node && node < getnnodes(tcliquegraph)); range = weights[node]; assert(range > 0); iscolored[nodeVindex] = TRUE; debugMessage("%d. node choosen: vindex=%d, vertex=%d, satdeg=%d, range=%d, growclique=%u, weight=%d)\n", i+2, nodeVindex, node, gsd[nodeVindex].satdeg, range, growclique, weightcurrentclique); /* set apriori bound: apbound(v_i) = satdeg(v_i) + weight(v_i) */ apbound[nodeVindex] = gsd[nodeVindex].satdeg + range; assert(apbound[nodeVindex] > 0); /* update maximum saturation degree: maxsatdeg = max { satdeg(v_i) + weight(v_i) | v_i in V } */ if( maxsatdegree < apbound[nodeVindex] ) maxsatdegree = apbound[nodeVindex]; /* update clique */ if( gsd[nodeVindex].satdeg == 0 ) { /* current node is not adjacent to nodes of current clique, * i.e. current clique can not be increased */ debugMessage("current node not adjacend to current clique (weight:%d) -> starting new clique\n", weightcurrentclique); /* check, if weight of current clique is larger than weight of maximum weight clique found so far */ if( weightcurrentclique > *weightclique ) { int* tmp; /* update maximum weight clique found so far */ assert((workclique == clique) != (currentclique == clique)); tmp = workclique; *weightclique = weightcurrentclique; *nclique = ncurrentclique; workclique = currentclique; currentclique = tmp; assert((workclique == clique) != (currentclique == clique)); } weightcurrentclique = 0; ncurrentclique = 0; growclique = TRUE; } if( growclique ) { /* check, if the current node is still adjacent to all nodes in the clique */ if( gsd[nodeVindex].satdeg == weightcurrentclique ) { assert(ncurrentclique < nV); currentclique[ncurrentclique] = node; ncurrentclique++; weightcurrentclique += range; #ifdef TCLIQUE_DEBUG { int k; debugMessage("current clique (size:%d, weight:%d):", ncurrentclique, weightcurrentclique); for( k = 0; k < ncurrentclique; ++k ) debugPrintf(" %d", currentclique[k]); debugPrintf("\n"); } #endif } else { debugMessage("node satdeg: %d, clique weight: %d -> stop growing clique\n", gsd[nodeVindex].satdeg, weightcurrentclique); growclique = FALSE; } } /* search for fitting color intervals for current node */ pnc = &nwcitv; if( gsd[nodeVindex].lcitv == NULL ) { /* current node has no colored neighbors yet: create new color interval [1,range] */ ALLOC_ABORT( BMSallocChunkMemory(mem, &colorinterval) ); colorinterval->next = NULL; colorinterval->itv.inf = 1; colorinterval->itv.sup = range; /* add the new colorinterval [1, range] to the list of chosen colorintervals for node */ pnc->next = colorinterval; pnc = colorinterval; } else { int tocolor; int dif; /* current node has colored neighbors */ tocolor = range; lcitv = gsd[nodeVindex].lcitv; /* check, if first neighbor color interval [inf, sup] has inf > 1 */ if( lcitv->itv.inf != 1 ) { /* create new interval [1, min{range, inf}] */ dif = lcitv->itv.inf - 1 ; if( dif > tocolor ) dif = tocolor; ALLOC_ABORT( BMSallocChunkMemory(mem, &colorinterval) ); colorinterval->next = NULL; colorinterval->itv.inf = 1; colorinterval->itv.sup = dif; tocolor -= dif; pnc->next = colorinterval; pnc = colorinterval; } /* as long as node is not colored with all colors, create new color interval by filling * the gaps in the existing neighbor color intervals of the neighbors of node */ while( tocolor > 0 ) { dif = tocolor; ALLOC_ABORT( BMSallocChunkMemory(mem, &colorinterval) ); colorinterval->next = NULL; colorinterval->itv.inf = lcitv->itv.sup+1; if( lcitv->next != NULL ) { int min; min = lcitv->next->itv.inf - lcitv->itv.sup - 1; if( dif > min ) dif = min; lcitv = lcitv->next; } colorinterval->itv.sup = colorinterval->itv.inf + dif - 1; tocolor -= dif; pnc->next = colorinterval; pnc = colorinterval; } } debugMessage("-> updated neighbors:\n"); /* update saturation degree and neighbor colorintervals of all neighbors of node */ Vadj = buffer; nVadj = selectadjnodes(tcliquegraph, node, V, nV, Vadj); for( j = 0, adjidx = 0; j < nV && adjidx < nVadj; ++j ) { assert(V[j] <= Vadj[adjidx]); /* Vadj is a subset of V */ if( V[j] == Vadj[adjidx] ) { if( !iscolored[j] ) { debugMessage(" nodeVindex=%d, node=%d, weight=%d, satdegold=%d -> ", j, V[j], weights[V[j]], gsd[j].satdeg); updateNeighbor(mem, &gsd[j], nwcitv.next); debugPrintf("satdegnew=%d, nbc=[%d,%d]\n", gsd[j].satdeg, gsd[j].lcitv->itv.inf, gsd[j].lcitv->itv.sup); } /* go to the next node in Vadj */ adjidx++; } } /* free data structure of created colorintervals */ item = nwcitv.next; while( item != NULL ) { tmpitem = item->next; BMSfreeChunkMemory(mem, &item); item = tmpitem; } /* free data structure of neighbor colorinterval of node just colored */ item = gsd[nodeVindex].lcitv; while( item != NULL ) { tmpitem = item->next; BMSfreeChunkMemory(mem, &item); item = tmpitem; } } assert((workclique == clique) != (currentclique == clique)); /* update maximum weight clique found so far */ if( weightcurrentclique > *weightclique ) { int* tmp; tmp = workclique; *weightclique = weightcurrentclique; *nclique = ncurrentclique; workclique = currentclique; currentclique = tmp; } assert((workclique == clique) != (currentclique == clique)); /* move the found clique to the provided clique pointer, if it is not the memory array */ if( workclique != clique ) { assert(clique == currentclique); assert(*nclique <= nV); BMScopyMemoryArray(clique, workclique, *nclique); currentclique = workclique; } /* free data structures */ BMSfreeMemoryArray(¤tclique); /* clear chunk memory */ BMSclearChunkMemory(mem); debugMessage("------------coloringend-----------------\n"); return maxsatdegree; }
/** performs the all fullstrong branching */ static SCIP_RETCODE branch( SCIP* scip, /**< SCIP data structure */ SCIP_BRANCHRULE* branchrule, /**< branching rule */ SCIP_Bool allowaddcons, /**< should adding constraints be allowed to avoid a branching? */ SCIP_RESULT* result /**< pointer to store the result of the callback method */ ) { SCIP_BRANCHRULEDATA* branchruledata; SCIP_VAR** pseudocands; SCIP_Real bestdown; SCIP_Real bestup; SCIP_Real bestscore; SCIP_Real provedbound; SCIP_Bool exactsolve; SCIP_Bool allcolsinlp; SCIP_Bool bestdownvalid; SCIP_Bool bestupvalid; int npseudocands; int npriopseudocands; int bestpseudocand; #ifndef NDEBUG SCIP_Real cutoffbound; cutoffbound = SCIPgetCutoffbound(scip); #endif assert(branchrule != NULL); assert(strcmp(SCIPbranchruleGetName(branchrule), BRANCHRULE_NAME) == 0); assert(scip != NULL); assert(result != NULL); /* check, if all existing columns are in LP, and thus the strong branching results give lower bounds */ allcolsinlp = SCIPallColsInLP(scip); /* check, if we want to solve the problem exactly, meaning that strong branching information is not useful * for cutting off sub problems and improving lower bounds of children */ exactsolve = SCIPisExactSolve(scip); /* get branching rule data */ branchruledata = SCIPbranchruleGetData(branchrule); assert(branchruledata != NULL); if( branchruledata->skipdown == NULL ) { int nvars; nvars = SCIPgetNVars(scip); assert(branchruledata->skipup == NULL); SCIP_CALL( SCIPallocMemoryArray(scip, &branchruledata->skipdown, nvars) ); SCIP_CALL( SCIPallocMemoryArray(scip, &branchruledata->skipup, nvars) ); BMSclearMemoryArray(branchruledata->skipdown, nvars); BMSclearMemoryArray(branchruledata->skipup, nvars); } /* get all non-fixed variables (not only the fractional ones) */ SCIP_CALL( SCIPgetPseudoBranchCands(scip, &pseudocands, &npseudocands, &npriopseudocands) ); assert(npseudocands > 0); assert(npriopseudocands > 0); SCIP_CALL( SCIPselectVarPseudoStrongBranching(scip, pseudocands, branchruledata->skipdown, branchruledata->skipup, npseudocands, npriopseudocands, allowaddcons, &bestpseudocand, &bestdown, &bestup, &bestscore, &bestdownvalid, &bestupvalid, &provedbound, result) ); if( *result != SCIP_CUTOFF && *result != SCIP_REDUCEDDOM && *result != SCIP_CONSADDED ) { SCIP_NODE* downchild; SCIP_NODE* eqchild; SCIP_NODE* upchild; SCIP_VAR* var; assert(*result == SCIP_DIDNOTRUN); assert(0 <= bestpseudocand && bestpseudocand < npseudocands); assert(SCIPisLT(scip, provedbound, cutoffbound)); var = pseudocands[bestpseudocand]; /* perform the branching */ SCIPdebugMessage(" -> %d candidates, selected candidate %d: variable <%s>[%g,%g] (solval=%g, down=%g, up=%g, score=%g)\n", npseudocands, bestpseudocand, SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var), SCIPvarGetLPSol(var), bestdown, bestup, bestscore); SCIP_CALL( SCIPbranchVarVal(scip, var, SCIPvarGetLPSol(var), &downchild, &eqchild, &upchild) ); /* update the lower bounds in the children */ if( allcolsinlp && !exactsolve ) { if( downchild != NULL ) { SCIP_CALL( SCIPupdateNodeLowerbound(scip, downchild, bestdownvalid ? MAX(bestdown, provedbound) : provedbound) ); SCIPdebugMessage(" -> down child's lowerbound: %g\n", SCIPnodeGetLowerbound(downchild)); } if( eqchild != NULL ) { SCIP_CALL( SCIPupdateNodeLowerbound(scip, eqchild, provedbound) ); SCIPdebugMessage(" -> eq child's lowerbound: %g\n", SCIPnodeGetLowerbound(eqchild)); } if( upchild != NULL ) { SCIP_CALL( SCIPupdateNodeLowerbound(scip, upchild, bestupvalid ? MAX(bestup, provedbound) : provedbound) ); SCIPdebugMessage(" -> up child's lowerbound: %g\n", SCIPnodeGetLowerbound(upchild)); } } *result = SCIP_BRANCHED; } return SCIP_OKAY; }
/** 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; }
/** reads the next line from the input file into the line buffer; skips comments; * returns whether a line could be read */ static SCIP_Bool getNextLine( SCIP* scip, /**< SCIP data structure */ LPINPUT* lpinput /**< LP reading data */ ) { int i; assert(lpinput != NULL); /* if we previously detected a comment we have to parse the remaining line away if there is something left */ if( !lpinput->endline && lpinput->comment ) { SCIPdebugMessage("Throwing rest of comment away.\n"); do { lpinput->linebuf[LP_MAX_LINELEN-2] = '\0'; (void)SCIPfgets(lpinput->linebuf, (int) sizeof(lpinput->linebuf), lpinput->file); } while( lpinput->linebuf[LP_MAX_LINELEN-2] != '\0' ); lpinput->comment = FALSE; lpinput->endline = TRUE; } /* read next line */ lpinput->linepos = 0; lpinput->linebuf[LP_MAX_LINELEN-2] = '\0'; if( SCIPfgets(lpinput->linebuf, (int) sizeof(lpinput->linebuf), lpinput->file) == NULL ) { /* clear the line, this is really necessary here! */ BMSclearMemoryArray(lpinput->linebuf, LP_MAX_LINELEN); return FALSE; } lpinput->linenumber++; /* if line is too long for our buffer correct the buffer and correct position in file */ if( lpinput->linebuf[LP_MAX_LINELEN-2] != '\0' ) { char* last; /* buffer is full; erase last token since it might be incomplete */ lpinput->endline = FALSE; last = strrchr(lpinput->linebuf, ' '); if( last == NULL ) { SCIPwarningMessage(scip, "we read %d characters from the file; this might indicate a corrupted input file!", LP_MAX_LINELEN - 2); lpinput->linebuf[LP_MAX_LINELEN-2] = '\0'; SCIPdebugMessage("the buffer might be corrupted\n"); } else { SCIPfseek(lpinput->file, -(long) strlen(last) - 1, SEEK_CUR); SCIPdebugMessage("correct buffer, reread the last %ld characters\n", (long) strlen(last) + 1); *last = '\0'; } } else { /* found end of line */ lpinput->endline = TRUE; } lpinput->linebuf[LP_MAX_LINELEN-1] = '\0'; /* we want to use lookahead of one char -> we need two \0 at the end */ lpinput->comment = FALSE; /* skip characters after comment symbol */ for( i = 0; commentchars[i] != '\0'; ++i ) { char* commentstart; commentstart = strchr(lpinput->linebuf, commentchars[i]); if( commentstart != NULL ) { *commentstart = '\0'; *(commentstart+1) = '\0'; /* we want to use lookahead of one char -> we need two \0 at the end */ lpinput->comment = TRUE; break; } } return TRUE; }
/** execution method of primal heuristic */ static SCIP_DECL_HEUREXEC(heurExecRootsoldiving) /*lint --e{715}*/ { /*lint --e{715}*/ SCIP_HEURDATA* heurdata; SCIP_VAR** vars; SCIP_Real* rootsol; SCIP_Real* objchgvals; int* softroundings; int* intvalrounds; int nvars; int nbinvars; int nintvars; int nlpcands; SCIP_LPSOLSTAT lpsolstat; SCIP_Real absstartobjval; SCIP_Real objstep; SCIP_Real alpha; SCIP_Real oldobj; SCIP_Real newobj; SCIP_Bool lperror; SCIP_Bool lpsolchanged; SCIP_Longint nsolsfound; SCIP_Longint ncalls; SCIP_Longint nlpiterations; SCIP_Longint maxnlpiterations; int depth; int maxdepth; int maxdivedepth; int divedepth; int startnlpcands; int ncycles; int i; 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; /* only call heuristic, if the LP solution is basic (which allows fast resolve in diving) */ if( !SCIPisLPSolBasic(scip) ) return SCIP_OKAY; /* don't dive two times at the same node */ if( SCIPgetLastDivenode(scip) == SCIPgetNNodes(scip) && SCIPgetDepth(scip) > 0 ) return SCIP_OKAY; *result = SCIP_DIDNOTRUN; /* get heuristic's data */ heurdata = SCIPheurGetData(heur); assert(heurdata != NULL); /* only apply heuristic, if only a few solutions have been found */ if( heurdata->maxsols >= 0 && SCIPgetNSolsFound(scip) >= heurdata->maxsols ) return SCIP_OKAY; /* only try to dive, if we are in the correct part of the tree, given by minreldepth and maxreldepth */ depth = SCIPgetDepth(scip); maxdepth = SCIPgetMaxDepth(scip); maxdepth = MAX(maxdepth, 30); if( depth < heurdata->minreldepth*maxdepth || depth > heurdata->maxreldepth*maxdepth ) return SCIP_OKAY; /* calculate the maximal number of LP iterations until heuristic is aborted */ nlpiterations = SCIPgetNNodeLPIterations(scip); ncalls = SCIPheurGetNCalls(heur); nsolsfound = 10*SCIPheurGetNBestSolsFound(heur) + heurdata->nsuccess; maxnlpiterations = (SCIP_Longint)((1.0 + 10.0*(nsolsfound+1.0)/(ncalls+1.0)) * heurdata->maxlpiterquot * nlpiterations); maxnlpiterations += heurdata->maxlpiterofs; /* don't try to dive, if we took too many LP iterations during diving */ if( heurdata->nlpiterations >= maxnlpiterations ) return SCIP_OKAY; /* allow at least a certain number of LP iterations in this dive */ maxnlpiterations = MAX(maxnlpiterations, heurdata->nlpiterations + MINLPITER); /* get number of fractional variables, that should be integral */ nlpcands = SCIPgetNLPBranchCands(scip); /* don't try to dive, if there are no fractional variables */ if( nlpcands == 0 ) return SCIP_OKAY; /* calculate the maximal diving depth */ nvars = SCIPgetNBinVars(scip) + SCIPgetNIntVars(scip); if( SCIPgetNSolsFound(scip) == 0 ) maxdivedepth = (int)(heurdata->depthfacnosol * nvars); else maxdivedepth = (int)(heurdata->depthfac * nvars); maxdivedepth = MAX(maxdivedepth, 10); *result = SCIP_DIDNOTFIND; /* get all variables of LP */ SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, &nbinvars, &nintvars, NULL, NULL) ); /* get root solution value of all binary and integer variables */ SCIP_CALL( SCIPallocBufferArray(scip, &rootsol, nbinvars + nintvars) ); for( i = 0; i < nbinvars + nintvars; i++ ) rootsol[i] = SCIPvarGetRootSol(vars[i]); /* get current LP objective value, and calculate length of a single step in an objective coefficient */ absstartobjval = SCIPgetLPObjval(scip); absstartobjval = ABS(absstartobjval); absstartobjval = MAX(absstartobjval, 1.0); objstep = absstartobjval / 10.0; /* initialize array storing the preferred soft rounding directions and counting the integral value rounds */ SCIP_CALL( SCIPallocBufferArray(scip, &softroundings, nbinvars + nintvars) ); BMSclearMemoryArray(softroundings, nbinvars + nintvars); SCIP_CALL( SCIPallocBufferArray(scip, &intvalrounds, nbinvars + nintvars) ); BMSclearMemoryArray(intvalrounds, nbinvars + nintvars); /* allocate temporary memory for buffering objective changes */ SCIP_CALL( SCIPallocBufferArray(scip, &objchgvals, nbinvars + nintvars) ); /* start diving */ SCIP_CALL( SCIPstartDive(scip) ); SCIPdebugMessage("(node %"SCIP_LONGINT_FORMAT") executing rootsoldiving heuristic: depth=%d, %d fractionals, dualbound=%g, maxnlpiterations=%"SCIP_LONGINT_FORMAT", maxdivedepth=%d, LPobj=%g, objstep=%g\n", SCIPgetNNodes(scip), SCIPgetDepth(scip), nlpcands, SCIPgetDualbound(scip), maxnlpiterations, maxdivedepth, SCIPgetLPObjval(scip), objstep); lperror = FALSE; divedepth = 0; lpsolstat = SCIP_LPSOLSTAT_OPTIMAL; alpha = heurdata->alpha; ncycles = 0; lpsolchanged = TRUE; startnlpcands = nlpcands; while( !lperror && lpsolstat == SCIP_LPSOLSTAT_OPTIMAL && nlpcands > 0 && ncycles < 10 && (divedepth < 10 || nlpcands <= startnlpcands - divedepth/2 || (divedepth < maxdivedepth && heurdata->nlpiterations < maxnlpiterations)) && !SCIPisStopped(scip) ) { SCIP_Bool success; int hardroundingidx; int hardroundingdir; SCIP_Real hardroundingoldbd; SCIP_Real hardroundingnewbd; SCIP_Bool boundschanged; SCIP_RETCODE retcode; /* create solution from diving LP and try to round it */ SCIP_CALL( SCIPlinkLPSol(scip, heurdata->sol) ); SCIP_CALL( SCIProundSol(scip, heurdata->sol, &success) ); if( success ) { SCIPdebugMessage("rootsoldiving found roundable primal solution: obj=%g\n", SCIPgetSolOrigObj(scip, heurdata->sol)); /* try to add solution to SCIP */ SCIP_CALL( SCIPtrySol(scip, heurdata->sol, FALSE, FALSE, FALSE, FALSE, &success) ); /* check, if solution was feasible and good enough */ if( success ) { SCIPdebugMessage(" -> solution was feasible and good enough\n"); *result = SCIP_FOUNDSOL; } } divedepth++; hardroundingidx = -1; hardroundingdir = 0; hardroundingoldbd = 0.0; hardroundingnewbd = 0.0; boundschanged = FALSE; SCIPdebugMessage("dive %d/%d, LP iter %"SCIP_LONGINT_FORMAT"/%"SCIP_LONGINT_FORMAT":\n", divedepth, maxdivedepth, heurdata->nlpiterations, maxnlpiterations); /* round solution x* from diving LP: * - x~_j = down(x*_j) if x*_j is integer or binary variable and x*_j <= root solution_j * - x~_j = up(x*_j) if x*_j is integer or binary variable and x*_j > root solution_j * - x~_j = x*_j if x*_j is continuous variable * change objective function in diving LP: * - if x*_j is integral, or j is a continuous variable, set obj'_j = alpha * obj_j * - otherwise, set obj'_j = alpha * obj_j + sign(x*_j - x~_j) */ for( i = 0; i < nbinvars + nintvars; i++ ) { SCIP_VAR* var; SCIP_Real solval; var = vars[i]; oldobj = SCIPgetVarObjDive(scip, var); newobj = oldobj; solval = SCIPvarGetLPSol(var); if( SCIPisFeasIntegral(scip, solval) ) { /* if the variable became integral after a soft rounding, count the rounds; after a while, fix it to its * current integral value; * otherwise, fade out the objective value */ if( softroundings[i] != 0 && lpsolchanged ) { intvalrounds[i]++; if( intvalrounds[i] == 5 && SCIPgetVarLbDive(scip, var) < SCIPgetVarUbDive(scip, var) - 0.5 ) { /* use exact integral value, if the variable is only integral within numerical tolerances */ solval = SCIPfloor(scip, solval+0.5); SCIPdebugMessage(" -> fixing <%s> = %g\n", SCIPvarGetName(var), solval); SCIP_CALL( SCIPchgVarLbDive(scip, var, solval) ); SCIP_CALL( SCIPchgVarUbDive(scip, var, solval) ); boundschanged = TRUE; } } else newobj = alpha * oldobj; } else if( solval <= rootsol[i] ) { /* if the variable was soft rounded most of the time downwards, round it downwards by changing the bounds; * otherwise, apply soft rounding by changing the objective value */ softroundings[i]--; if( softroundings[i] <= -10 && hardroundingidx == -1 ) { SCIPdebugMessage(" -> hard rounding <%s>[%g] <= %g\n", SCIPvarGetName(var), solval, SCIPfeasFloor(scip, solval)); hardroundingidx = i; hardroundingdir = -1; hardroundingoldbd = SCIPgetVarUbDive(scip, var); hardroundingnewbd = SCIPfeasFloor(scip, solval); SCIP_CALL( SCIPchgVarUbDive(scip, var, hardroundingnewbd) ); boundschanged = TRUE; } else newobj = alpha * oldobj + objstep; } else { /* if the variable was soft rounded most of the time upwards, round it upwards by changing the bounds; * otherwise, apply soft rounding by changing the objective value */ softroundings[i]++; if( softroundings[i] >= +10 && hardroundingidx == -1 ) { SCIPdebugMessage(" -> hard rounding <%s>[%g] >= %g\n", SCIPvarGetName(var), solval, SCIPfeasCeil(scip, solval)); hardroundingidx = i; hardroundingdir = +1; hardroundingoldbd = SCIPgetVarLbDive(scip, var); hardroundingnewbd = SCIPfeasCeil(scip, solval); SCIP_CALL( SCIPchgVarLbDive(scip, var, hardroundingnewbd) ); boundschanged = TRUE; } else newobj = alpha * oldobj - objstep; } /* remember the objective change */ objchgvals[i] = newobj; } /* apply objective changes if there was no bound change */ if( !boundschanged ) { /* apply cached changes on integer variables */ for( i = 0; i < nbinvars + nintvars; ++i ) { SCIP_VAR* var; var = vars[i]; SCIPdebugMessage(" -> i=%d var <%s>, solval=%g, rootsol=%g, oldobj=%g, newobj=%g\n", i, SCIPvarGetName(var), SCIPvarGetLPSol(var), rootsol[i], SCIPgetVarObjDive(scip, var), objchgvals[i]); SCIP_CALL( SCIPchgVarObjDive(scip, var, objchgvals[i]) ); } /* fade out the objective values of the continuous variables */ for( i = nbinvars + nintvars; i < nvars; i++ ) { SCIP_VAR* var; var = vars[i]; oldobj = SCIPgetVarObjDive(scip, var); newobj = alpha * oldobj; SCIPdebugMessage(" -> i=%d var <%s>, solval=%g, oldobj=%g, newobj=%g\n", i, SCIPvarGetName(var), SCIPvarGetLPSol(var), oldobj, newobj); SCIP_CALL( SCIPchgVarObjDive(scip, var, newobj) ); } } SOLVEAGAIN: /* resolve the diving LP */ nlpiterations = SCIPgetNLPIterations(scip); retcode = SCIPsolveDiveLP(scip, MAX((int)(maxnlpiterations - heurdata->nlpiterations), MINLPITER), &lperror); lpsolstat = SCIPgetLPSolstat(scip); /* 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. */ if( retcode != SCIP_OKAY ) { #ifndef NDEBUG if( lpsolstat != SCIP_LPSOLSTAT_UNBOUNDEDRAY ) { SCIP_CALL( retcode ); } #endif SCIPwarningMessage(scip, "Error while solving LP in Rootsoldiving heuristic; LP solve terminated with code <%d>\n", retcode); SCIPwarningMessage(scip, "This does not affect the remaining solution procedure --> continue\n"); } if( lperror ) break; /* update iteration count */ heurdata->nlpiterations += SCIPgetNLPIterations(scip) - nlpiterations; /* if no LP iterations were performed, we stayed at the same solution -> count this cycling */ lpsolchanged = (SCIPgetNLPIterations(scip) != nlpiterations); if( lpsolchanged ) ncycles = 0; else if( !boundschanged ) /* do not count if integral variables have been fixed */ ncycles++; /* get LP solution status and number of fractional variables, that should be integral */ if( lpsolstat == SCIP_LPSOLSTAT_INFEASIBLE && hardroundingidx != -1 ) { SCIP_VAR* var; var = vars[hardroundingidx]; /* round the hard rounded variable to the opposite direction and resolve the LP */ if( hardroundingdir == -1 ) { SCIPdebugMessage(" -> opposite hard rounding <%s> >= %g\n", SCIPvarGetName(var), hardroundingnewbd + 1.0); SCIP_CALL( SCIPchgVarUbDive(scip, var, hardroundingoldbd) ); SCIP_CALL( SCIPchgVarLbDive(scip, var, hardroundingnewbd + 1.0) ); } else { SCIPdebugMessage(" -> opposite hard rounding <%s> <= %g\n", SCIPvarGetName(var), hardroundingnewbd - 1.0); SCIP_CALL( SCIPchgVarLbDive(scip, var, hardroundingoldbd) ); SCIP_CALL( SCIPchgVarUbDive(scip, var, hardroundingnewbd - 1.0) ); } hardroundingidx = -1; goto SOLVEAGAIN; } if( lpsolstat == SCIP_LPSOLSTAT_OPTIMAL ) nlpcands = SCIPgetNLPBranchCands(scip); SCIPdebugMessage(" -> lpsolstat=%d, nfrac=%d\n", lpsolstat, nlpcands); } SCIPdebugMessage("---> diving finished: lpsolstat = %d, depth %d/%d, LP iter %"SCIP_LONGINT_FORMAT"/%"SCIP_LONGINT_FORMAT"\n", lpsolstat, divedepth, maxdivedepth, heurdata->nlpiterations, maxnlpiterations); /* check if a solution has been found */ if( nlpcands == 0 && !lperror && lpsolstat == SCIP_LPSOLSTAT_OPTIMAL ) { SCIP_Bool success; /* create solution from diving LP */ SCIP_CALL( SCIPlinkLPSol(scip, heurdata->sol) ); SCIPdebugMessage("rootsoldiving found primal solution: obj=%g\n", SCIPgetSolOrigObj(scip, heurdata->sol)); /* try to add solution to SCIP */ SCIP_CALL( SCIPtrySol(scip, heurdata->sol, FALSE, FALSE, FALSE, FALSE, &success) ); /* check, if solution was feasible and good enough */ if( success ) { SCIPdebugMessage(" -> solution was feasible and good enough\n"); *result = SCIP_FOUNDSOL; } } /* end diving */ SCIP_CALL( SCIPendDive(scip) ); if( *result == SCIP_FOUNDSOL ) heurdata->nsuccess++; /* free temporary memory */ SCIPfreeBufferArray(scip, &objchgvals); SCIPfreeBufferArray(scip, &intvalrounds); SCIPfreeBufferArray(scip, &softroundings); SCIPfreeBufferArray(scip, &rootsol); SCIPdebugMessage("rootsoldiving heuristic finished\n"); return SCIP_OKAY; }