/** adds a given value to the fractionality counters of the rows in which the given variable appears */ static void addFracCounter( int* nfracsinrow, /**< array to store number of fractional variables per row */ int nlprows, /**< number of rows in LP */ SCIP_VAR* var, /**< variable for which the counting should be updated */ int incval /**< value that should be added to the corresponding array entries */ ) { SCIP_COL* col; SCIP_ROW** rows; int nrows; int r; col = SCIPvarGetCol(var); rows = SCIPcolGetRows(col); nrows = SCIPcolGetNLPNonz(col); for( r = 0; r < nrows; ++r ) { int rowidx; rowidx = SCIProwGetLPPos(rows[r]); assert(0 <= rowidx && rowidx < nlprows); nfracsinrow[rowidx] += incval; assert(nfracsinrow[rowidx] >= 0); } }
/** finds a continuous slack variable for an equation row, NULL if none exists */ static void rowFindSlackVar( SCIP* scip, /**< pointer to current SCIP data structure */ SCIP_ROW* row, /**< the row for which a slack variable is searched */ SCIP_VAR** varpointer, /**< pointer to store the slack variable */ SCIP_Real* coeffpointer /**< pointer to store the coefficient of the slack variable */ ) { int v; SCIP_COL** rowcols; SCIP_Real* rowvals; int nrowvals; assert(row != NULL); assert(varpointer != NULL); assert(coeffpointer != NULL); rowcols = SCIProwGetCols(row); rowvals = SCIProwGetVals(row); nrowvals = SCIProwGetNNonz(row); assert(nrowvals == 0 || rowvals != NULL); assert(nrowvals == 0 || rowcols != NULL); /* iterate over the row variables. Stop after the first unfixed continuous variable was found. */ for( v = nrowvals - 1; v >= 0; --v ) { SCIP_VAR* colvar; assert(rowcols[v] != NULL); if( SCIPcolGetLPPos(rowcols[v]) == -1 ) continue; colvar = SCIPcolGetVar(rowcols[v]); if( SCIPvarGetType(colvar) == SCIP_VARTYPE_CONTINUOUS && !SCIPisFeasEQ(scip, SCIPvarGetLbGlobal(colvar), SCIPvarGetUbGlobal(colvar)) && SCIPcolGetNLPNonz(rowcols[v]) == 1 ) { SCIPdebugMessage(" slack variable for row %s found: %s\n", SCIProwGetName(row), SCIPvarGetName(colvar)); *coeffpointer = rowvals[v]; *varpointer = colvar; return; } } *varpointer = NULL; *coeffpointer = 0.0; SCIPdebugMessage("No slack variable for row %s found. \n", SCIProwGetName(row)); }
/** update row activities after a variable's solution value changed */ static SCIP_RETCODE updateRowActivities( SCIP* scip, /**< SCIP data structure */ SCIP_Real* activities, /**< LP row activities */ SCIP_VAR* var, /**< variable that has been changed */ SCIP_Real shiftval /**< value that is added to variable */ ) { SCIP_Real* colvals; SCIP_ROW** colrows; SCIP_COL* col; int ncolrows; int i; assert(activities != NULL); /* get data of column associated to variable */ col = SCIPvarGetCol(var); colrows = SCIPcolGetRows(col); colvals = SCIPcolGetVals(col); ncolrows = SCIPcolGetNLPNonz(col); assert(ncolrows == 0 || (colrows != NULL && colvals != NULL)); /* enumerate all rows with nonzero entry in this column */ for( i = 0; i < ncolrows; ++i ) { SCIP_ROW* row; int rowpos; row = colrows[i]; rowpos = SCIProwGetLPPos(row); assert(-1 <= rowpos && rowpos < SCIPgetNLPRows(scip) ); /* update row activity, only regard global rows in the LP */ if( rowpos >= 0 && !SCIProwIsLocal(row) ) { activities[rowpos] += shiftval * colvals[i]; if( SCIPisInfinity(scip, activities[rowpos]) ) activities[rowpos] = SCIPinfinity(scip); else if( SCIPisInfinity(scip, -activities[rowpos]) ) activities[rowpos] = -SCIPinfinity(scip); } } 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; }
/** determines shifting bounds for variable */ static void calculateBounds( SCIP* scip, /**< pointer to current SCIP data structure */ SCIP_VAR* var, /**< the variable for which lb and ub have to be calculated */ SCIP_Real currentvalue, /**< the current value of var in the working solution */ SCIP_Real* upperbound, /**< pointer to store the calculated upper bound on the variable shift */ SCIP_Real* lowerbound, /**< pointer to store the calculated lower bound on the variable shift */ SCIP_Real* upslacks, /**< array that contains the slacks between row activities and the right hand sides of the rows */ SCIP_Real* downslacks, /**< array that contains lhs slacks */ int nslacks, /**< current number of slacks */ SCIP_Bool* numericalerror /**< flag to determine whether a numerical error occurred */ ) { SCIP_COL* col; SCIP_ROW** colrows; SCIP_Real* colvals; int ncolvals; int i; assert(scip != NULL); assert(var != NULL); assert(upslacks != NULL); assert(downslacks != NULL); assert(upperbound != NULL); assert(lowerbound != NULL); /* get the column associated to the variable, the nonzero rows and the nonzero coefficients */ col = SCIPvarGetCol(var); colrows = SCIPcolGetRows(col); colvals = SCIPcolGetVals(col); ncolvals = SCIPcolGetNLPNonz(col); /* only proceed, when variable has nonzero coefficients */ if( ncolvals == 0 ) return; assert(colvals != NULL); assert(colrows != NULL); /* initialize the bounds on the shift to be the gap of the current solution value to the bounds of the variable */ if( SCIPisInfinity(scip, SCIPvarGetUbGlobal(var)) ) *upperbound = SCIPinfinity(scip); else *upperbound = SCIPvarGetUbGlobal(var) - currentvalue; if( SCIPisInfinity(scip, -SCIPvarGetLbGlobal(var)) ) *lowerbound = SCIPinfinity(scip); else *lowerbound = currentvalue - SCIPvarGetLbGlobal(var); /* go through every nonzero row coefficient corresponding to var to determine bounds for shifting * in such a way that shifting maintains feasibility in every LP row. * a lower or upper bound as it is calculated in zirounding always has to be >= 0.0. * if one of these values is significantly < 0.0, this will cause the abort of execution of the heuristic so that * infeasible solutions are avoided */ for( i = 0; i < ncolvals && (*lowerbound > 0.0 || *upperbound > 0.0); ++i ) { SCIP_ROW* row; int rowpos; row = colrows[i]; rowpos = SCIProwGetLPPos(row); /* the row might currently not be in the LP, ignore it! */ if( rowpos == -1 ) continue; assert(0 <= rowpos && rowpos < nslacks); /* all bounds and slacks as they are calculated in zirounding always have to be greater equal zero. * It might however be due to numerical issues, e.g. with scaling, that they are not. Better abort in this case. */ if( SCIPisFeasLT(scip, *lowerbound, 0.0) || SCIPisFeasLT(scip, *upperbound, 0.0) || SCIPisFeasLT(scip, upslacks[rowpos], 0.0) || SCIPisFeasLT(scip, downslacks[rowpos] , 0.0) ) { *numericalerror = TRUE; return; } SCIPdebugMessage("colval: %15.8g, downslack: %15.8g, upslack: %5.2g, lb: %5.2g, ub: %5.2g\n", colvals[i], downslacks[rowpos], upslacks[rowpos], *lowerbound, *upperbound); /* if coefficient > 0, rounding up might violate up slack and rounding down might violate down slack * thus search for the minimum so that no constraint is violated; vice versa for coefficient < 0 */ if( colvals[i] > 0 ) { if( !SCIPisInfinity(scip, upslacks[rowpos]) ) { SCIP_Real upslack; upslack = MAX(upslacks[rowpos], 0.0); /* avoid errors due to numerically slightly infeasible rows */ *upperbound = MIN(*upperbound, upslack/colvals[i]); } if( !SCIPisInfinity(scip, downslacks[rowpos]) ) { SCIP_Real downslack; downslack = MAX(downslacks[rowpos], 0.0); /* avoid errors due to numerically slightly infeasible rows */ *lowerbound = MIN(*lowerbound, downslack/colvals[i]); } } else { assert(colvals[i] != 0.0); if( !SCIPisInfinity(scip, upslacks[rowpos]) ) { SCIP_Real upslack; upslack = MAX(upslacks[rowpos], 0.0); /* avoid errors due to numerically slightly infeasible rows */ *lowerbound = MIN(*lowerbound, -upslack/colvals[i]); } if( !SCIPisInfinity(scip, downslacks[rowpos]) ) { SCIP_Real downslack; downslack = MAX(downslacks[rowpos], 0.0); /* avoid errors due to numerically slightly infeasible rows */ *upperbound = MIN(*upperbound, -downslack/colvals[i]); } } } }
/** 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; }
/** 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; }
/** 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; }
/** update row activities after a variable's solution value changed */ static SCIP_RETCODE updateActivities( SCIP* scip, /**< SCIP data structure */ SCIP_Real* activities, /**< LP row activities */ 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 */ int nlprows, /**< number of rows in current LP */ SCIP_VAR* var, /**< variable that has been changed */ SCIP_Real oldsolval, /**< old solution value of variable */ SCIP_Real newsolval /**< new solution value of variable */ ) { SCIP_COL* col; SCIP_ROW** colrows; SCIP_Real* colvals; SCIP_Real delta; int ncolrows; int r; assert(activities != NULL); assert(nviolrows != NULL); assert(0 <= *nviolrows && *nviolrows <= nlprows); delta = newsolval - oldsolval; col = SCIPvarGetCol(var); colrows = SCIPcolGetRows(col); colvals = SCIPcolGetVals(col); ncolrows = SCIPcolGetNLPNonz(col); assert(ncolrows == 0 || (colrows != NULL && colvals != NULL)); for( r = 0; r < ncolrows; ++r ) { SCIP_ROW* row; int rowpos; row = colrows[r]; rowpos = SCIProwGetLPPos(row); assert(-1 <= rowpos && rowpos < nlprows); if( rowpos >= 0 && !SCIProwIsLocal(row) ) { SCIP_Real oldactivity; SCIP_Real newactivity; assert(SCIProwIsInLP(row)); /* update row activity */ oldactivity = activities[rowpos]; if( !SCIPisInfinity(scip, -oldactivity) && !SCIPisInfinity(scip, oldactivity) ) { newactivity = oldactivity + delta * colvals[r]; if( SCIPisInfinity(scip, newactivity) ) newactivity = SCIPinfinity(scip); else if( SCIPisInfinity(scip, -newactivity) ) newactivity = -SCIPinfinity(scip); activities[rowpos] = newactivity; /* update row violation arrays */ updateViolations(scip, row, violrows, violrowpos, nviolrows, oldactivity, newactivity); } } } return SCIP_OKAY; }