/** LP solution separation method for disjunctive cuts */ static SCIP_DECL_SEPAEXECLP(sepaExeclpDisjunctive) { SCIP_SEPADATA* sepadata; SCIP_CONSHDLR* conshdlr; SCIP_DIGRAPH* conflictgraph; SCIP_ROW** rows; SCIP_COL** cols; SCIP_Real* cutcoefs = NULL; SCIP_Real* simplexcoefs1 = NULL; SCIP_Real* simplexcoefs2 = NULL; SCIP_Real* coef = NULL; SCIP_Real* binvrow = NULL; SCIP_Real* rowsmaxval = NULL; SCIP_Real* violationarray = NULL; int* fixings1 = NULL; int* fixings2 = NULL; int* basisind = NULL; int* basisrow = NULL; int* varrank = NULL; int* edgearray = NULL; int nedges; int ndisjcuts; int nrelevantedges; int nsos1vars; int nconss; int maxcuts; int ncalls; int depth; int ncols; int nrows; int ind; int j; int i; assert( sepa != NULL ); assert( strcmp(SCIPsepaGetName(sepa), SEPA_NAME) == 0 ); assert( scip != NULL ); assert( result != NULL ); *result = SCIP_DIDNOTRUN; /* only generate disjunctive cuts if we are not close to terminating */ if ( SCIPisStopped(scip) ) return SCIP_OKAY; /* only generate disjunctive cuts if an optimal LP solution is at hand */ if ( SCIPgetLPSolstat(scip) != SCIP_LPSOLSTAT_OPTIMAL ) return SCIP_OKAY; /* only generate disjunctive cuts if the LP solution is basic */ if ( ! SCIPisLPSolBasic(scip) ) return SCIP_OKAY; /* get LP data */ SCIP_CALL( SCIPgetLPColsData(scip, &cols, &ncols) ); SCIP_CALL( SCIPgetLPRowsData(scip, &rows, &nrows) ); /* return if LP has no columns or no rows */ if ( ncols == 0 || nrows == 0 ) return SCIP_OKAY; assert( cols != NULL ); assert( rows != NULL ); /* get sepa data */ sepadata = SCIPsepaGetData(sepa); assert( sepadata != NULL ); /* get constraint handler */ conshdlr = sepadata->conshdlr; if ( conshdlr == NULL ) return SCIP_OKAY; /* get number of constraints */ nconss = SCIPconshdlrGetNConss(conshdlr); if ( nconss == 0 ) return SCIP_OKAY; /* check for maxdepth < depth, maxinvcutsroot = 0 and maxinvcuts = 0 */ depth = SCIPgetDepth(scip); if ( ( sepadata->maxdepth >= 0 && sepadata->maxdepth < depth ) || ( depth == 0 && sepadata->maxinvcutsroot == 0 ) || ( depth > 0 && sepadata->maxinvcuts == 0 ) ) return SCIP_OKAY; /* only call the cut separator a given number of times at each node */ ncalls = SCIPsepaGetNCallsAtNode(sepa); if ( (depth == 0 && sepadata->maxroundsroot >= 0 && ncalls >= sepadata->maxroundsroot) || (depth > 0 && sepadata->maxrounds >= 0 && ncalls >= sepadata->maxrounds) ) return SCIP_OKAY; /* get conflict graph and number of conflict graph edges (note that the digraph arcs were added in both directions) */ conflictgraph = SCIPgetConflictgraphSOS1(conshdlr); nedges = (int)SCIPceil(scip, (SCIP_Real)SCIPdigraphGetNArcs(conflictgraph)/2); /* if too many conflict graph edges, the separator can be slow: delay it until no other cuts have been found */ if ( sepadata->maxconfsdelay >= 0 && nedges >= sepadata->maxconfsdelay ) { int ncutsfound; ncutsfound = SCIPgetNCutsFound(scip); if ( ncutsfound > sepadata->lastncutsfound || ! SCIPsepaWasLPDelayed(sepa) ) { sepadata->lastncutsfound = ncutsfound; *result = SCIP_DELAYED; return SCIP_OKAY; } } /* check basis status */ for (j = 0; j < ncols; ++j) { if ( SCIPcolGetBasisStatus(cols[j]) == SCIP_BASESTAT_ZERO ) return SCIP_OKAY; } /* get number of SOS1 variables */ nsos1vars = SCIPgetNSOS1Vars(conshdlr); /* allocate buffer arrays */ SCIP_CALL( SCIPallocBufferArray(scip, &edgearray, nedges) ); SCIP_CALL( SCIPallocBufferArray(scip, &fixings1, nedges) ); SCIP_CALL( SCIPallocBufferArray(scip, &fixings2, nedges) ); SCIP_CALL( SCIPallocBufferArray(scip, &violationarray, nedges) ); /* get all violated conflicts {i, j} in the conflict graph and sort them based on the degree of a violation value */ nrelevantedges = 0; for (j = 0; j < nsos1vars; ++j) { SCIP_VAR* var; var = SCIPnodeGetVarSOS1(conflictgraph, j); if ( SCIPvarIsActive(var) && ! SCIPisFeasZero(scip, SCIPcolGetPrimsol(SCIPvarGetCol(var))) && SCIPcolGetBasisStatus(SCIPvarGetCol(var)) == SCIP_BASESTAT_BASIC ) { int* succ; int nsucc; /* get successors and number of successors */ nsucc = SCIPdigraphGetNSuccessors(conflictgraph, j); succ = SCIPdigraphGetSuccessors(conflictgraph, j); for (i = 0; i < nsucc; ++i) { SCIP_VAR* varsucc; int succind; succind = succ[i]; varsucc = SCIPnodeGetVarSOS1(conflictgraph, succind); if ( SCIPvarIsActive(varsucc) && succind < j && ! SCIPisFeasZero(scip, SCIPgetSolVal(scip, NULL, varsucc) ) && SCIPcolGetBasisStatus(SCIPvarGetCol(varsucc)) == SCIP_BASESTAT_BASIC ) { fixings1[nrelevantedges] = j; fixings2[nrelevantedges] = succind; edgearray[nrelevantedges] = nrelevantedges; violationarray[nrelevantedges++] = SCIPgetSolVal(scip, NULL, var) * SCIPgetSolVal(scip, NULL, varsucc); } } } } /* sort violation score values */ if ( nrelevantedges > 0) SCIPsortDownRealInt(violationarray, edgearray, nrelevantedges); else { SCIPfreeBufferArrayNull(scip, &violationarray); SCIPfreeBufferArrayNull(scip, &fixings2); SCIPfreeBufferArrayNull(scip, &fixings1); SCIPfreeBufferArrayNull(scip, &edgearray); return SCIP_OKAY; } SCIPfreeBufferArrayNull(scip, &violationarray); /* compute maximal number of cuts */ if ( SCIPgetDepth(scip) == 0 ) maxcuts = MIN(sepadata->maxinvcutsroot, nrelevantedges); else maxcuts = MIN(sepadata->maxinvcuts, nrelevantedges); assert( maxcuts > 0 ); /* allocate buffer arrays */ SCIP_CALL( SCIPallocBufferArray(scip, &varrank, ncols) ); SCIP_CALL( SCIPallocBufferArray(scip, &rowsmaxval, nrows) ); SCIP_CALL( SCIPallocBufferArray(scip, &basisrow, ncols) ); SCIP_CALL( SCIPallocBufferArray(scip, &binvrow, nrows) ); SCIP_CALL( SCIPallocBufferArray(scip, &coef, ncols) ); SCIP_CALL( SCIPallocBufferArray(scip, &simplexcoefs1, ncols) ); SCIP_CALL( SCIPallocBufferArray(scip, &simplexcoefs2, ncols) ); SCIP_CALL( SCIPallocBufferArray(scip, &cutcoefs, ncols) ); SCIP_CALL( SCIPallocBufferArray(scip, &basisind, nrows) ); /* get basis indices */ SCIP_CALL( SCIPgetLPBasisInd(scip, basisind) ); /* create vector "basisrow" with basisrow[column of non-slack basis variable] = corresponding row of B^-1; * compute maximum absolute value of nonbasic row coefficients */ for (j = 0; j < nrows; ++j) { SCIP_COL** rowcols; SCIP_Real* rowvals; SCIP_ROW* row; SCIP_Real val; SCIP_Real max = 0.0; int nnonz; /* fill basisrow vector */ ind = basisind[j]; if ( ind >= 0 ) basisrow[ind] = j; /* compute maximum absolute value of nonbasic row coefficients */ row = rows[j]; assert( row != NULL ); rowvals = SCIProwGetVals(row); nnonz = SCIProwGetNNonz(row); rowcols = SCIProwGetCols(row); for (i = 0; i < nnonz; ++i) { if ( SCIPcolGetBasisStatus(rowcols[i]) == SCIP_BASESTAT_LOWER || SCIPcolGetBasisStatus(rowcols[i]) == SCIP_BASESTAT_UPPER ) { val = REALABS(rowvals[i]); if ( SCIPisFeasGT(scip, val, max) ) max = REALABS(val); } } /* handle slack variable coefficient and save maximum value */ rowsmaxval[j] = MAX(max, 1.0); } /* initialize variable ranks with -1 */ for (j = 0; j < ncols; ++j) varrank[j] = -1; /* free buffer array */ SCIPfreeBufferArrayNull(scip, &basisind); /* for the most promising disjunctions: try to generate disjunctive cuts */ ndisjcuts = 0; for (i = 0; i < maxcuts; ++i) { SCIP_Bool madeintegral; SCIP_Real cutlhs1; SCIP_Real cutlhs2; SCIP_Real bound1; SCIP_Real bound2; SCIP_ROW* row = NULL; SCIP_VAR* var; SCIP_COL* col; int nonbasicnumber; int cutrank = 0; int edgenumber; int rownnonz; edgenumber = edgearray[i]; /* determine first simplex row */ var = SCIPnodeGetVarSOS1(conflictgraph, fixings1[edgenumber]); col = SCIPvarGetCol(var); ind = SCIPcolGetLPPos(col); assert( ind >= 0 ); assert( SCIPcolGetBasisStatus(col) == SCIP_BASESTAT_BASIC ); /* get the 'ind'th row of B^-1 and B^-1 \cdot A */ SCIP_CALL( SCIPgetLPBInvRow(scip, basisrow[ind], binvrow, NULL, NULL) ); SCIP_CALL( SCIPgetLPBInvARow(scip, basisrow[ind], binvrow, coef, NULL, NULL) ); /* get the simplex-coefficients of the non-basic variables */ SCIP_CALL( getSimplexCoefficients(scip, rows, nrows, cols, ncols, coef, binvrow, simplexcoefs1, &nonbasicnumber) ); /* get rank of variable if not known already */ if ( varrank[ind] < 0 ) varrank[ind] = getVarRank(scip, binvrow, rowsmaxval, sepadata->maxweightrange, rows, nrows); cutrank = MAX(cutrank, varrank[ind]); /* get right hand side and bound of simplex talbeau row */ cutlhs1 = SCIPcolGetPrimsol(col); if ( SCIPisFeasPositive(scip, cutlhs1) ) bound1 = SCIPcolGetUb(col); else bound1 = SCIPcolGetLb(col); /* determine second simplex row */ var = SCIPnodeGetVarSOS1(conflictgraph, fixings2[edgenumber]); col = SCIPvarGetCol(var); ind = SCIPcolGetLPPos(col); assert( ind >= 0 ); assert( SCIPcolGetBasisStatus(col) == SCIP_BASESTAT_BASIC ); /* get the 'ind'th row of B^-1 and B^-1 \cdot A */ SCIP_CALL( SCIPgetLPBInvRow(scip, basisrow[ind], binvrow, NULL, NULL) ); SCIP_CALL( SCIPgetLPBInvARow(scip, basisrow[ind], binvrow, coef, NULL, NULL) ); /* get the simplex-coefficients of the non-basic variables */ SCIP_CALL( getSimplexCoefficients(scip, rows, nrows, cols, ncols, coef, binvrow, simplexcoefs2, &nonbasicnumber) ); /* get rank of variable if not known already */ if ( varrank[ind] < 0 ) varrank[ind] = getVarRank(scip, binvrow, rowsmaxval, sepadata->maxweightrange, rows, nrows); cutrank = MAX(cutrank, varrank[ind]); /* get right hand side and bound of simplex talbeau row */ cutlhs2 = SCIPcolGetPrimsol(col); if ( SCIPisFeasPositive(scip, cutlhs2) ) bound2 = SCIPcolGetUb(col); else bound2 = SCIPcolGetLb(col); /* add coefficients to cut */ SCIP_CALL( generateDisjCutSOS1(scip, sepa, rows, nrows, cols, ncols, ndisjcuts, TRUE, sepadata->strengthen, cutlhs1, cutlhs2, bound1, bound2, simplexcoefs1, simplexcoefs2, cutcoefs, &row, &madeintegral) ); if ( row == NULL ) continue; /* raise cutrank for present cut */ ++cutrank; /* check if there are numerical evidences */ if ( ( madeintegral && ( sepadata->maxrankintegral == -1 || cutrank <= sepadata->maxrankintegral ) ) || ( ! madeintegral && ( sepadata->maxrank == -1 || cutrank <= sepadata->maxrank ) ) ) { /* possibly add cut to LP if it is useful; in case the lhs of the cut is minus infinity (due to scaling) the cut is useless */ rownnonz = SCIProwGetNNonz(row); if ( rownnonz > 0 && ! SCIPisInfinity(scip, -SCIProwGetLhs(row)) && ! SCIProwIsInLP(row) && SCIPisCutEfficacious(scip, NULL, row) ) { SCIP_Bool infeasible; /* set cut rank */ SCIProwChgRank(row, cutrank); /* add cut */ SCIP_CALL( SCIPaddCut(scip, NULL, row, FALSE, &infeasible) ); SCIPdebug( SCIP_CALL( SCIPprintRow(scip, row, NULL) ) ); if ( infeasible ) { *result = SCIP_CUTOFF; break; } ++ndisjcuts; } } /* release row */ SCIP_CALL( SCIPreleaseRow(scip, &row) ); } /* save total number of cuts found so far */ sepadata->lastncutsfound = SCIPgetNCutsFound(scip); /* evaluate the result of the separation */ if ( *result != SCIP_CUTOFF ) { if ( ndisjcuts > 0 ) *result = SCIP_SEPARATED; else *result = SCIP_DIDNOTFIND; } SCIPdebugMessage("Number of found disjunctive cuts: %d.\n", ndisjcuts); /* free buffer arrays */ SCIPfreeBufferArrayNull(scip, &cutcoefs); SCIPfreeBufferArrayNull(scip, &simplexcoefs2); SCIPfreeBufferArrayNull(scip, &simplexcoefs1); SCIPfreeBufferArrayNull(scip, &coef); SCIPfreeBufferArrayNull(scip, &binvrow); SCIPfreeBufferArrayNull(scip, &basisrow); SCIPfreeBufferArrayNull(scip, &fixings2); SCIPfreeBufferArrayNull(scip, &fixings1); SCIPfreeBufferArrayNull(scip, &edgearray); SCIPfreeBufferArrayNull(scip, &rowsmaxval); SCIPfreeBufferArrayNull(scip, &varrank); return SCIP_OKAY; }
/** 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; }
/** LP solution separation method of separator */ static SCIP_DECL_SEPAEXECLP(sepaExeclpStrongcg) { /*lint --e{715}*/ SCIP_SEPADATA* sepadata; SCIP_VAR** vars; SCIP_COL** cols; SCIP_ROW** rows; SCIP_Real* varsolvals; SCIP_Real* binvrow; SCIP_Real* cutcoefs; SCIP_Real cutrhs; SCIP_Real cutact; SCIP_Real maxscale; SCIP_Longint maxdnom; int* basisind; int* inds; int ninds; int nvars; int ncols; int nrows; int ncalls; int depth; int maxdepth; int maxsepacuts; int ncuts; int c; int i; int cutrank; SCIP_Bool success; SCIP_Bool cutislocal; char normtype; assert(sepa != NULL); assert(strcmp(SCIPsepaGetName(sepa), SEPA_NAME) == 0); assert(scip != NULL); assert(result != NULL); *result = SCIP_DIDNOTRUN; sepadata = SCIPsepaGetData(sepa); assert(sepadata != NULL); depth = SCIPgetDepth(scip); ncalls = SCIPsepaGetNCallsAtNode(sepa); /* only call separator, if we are not close to terminating */ if( SCIPisStopped(scip) ) return SCIP_OKAY; /* only call the strong CG cut separator a given number of times at each node */ if( (depth == 0 && sepadata->maxroundsroot >= 0 && ncalls >= sepadata->maxroundsroot) || (depth > 0 && sepadata->maxrounds >= 0 && ncalls >= sepadata->maxrounds) ) return SCIP_OKAY; /* only call separator, if an optimal LP solution is at hand */ if( SCIPgetLPSolstat(scip) != SCIP_LPSOLSTAT_OPTIMAL ) return SCIP_OKAY; /* only call separator, if the LP solution is basic */ if( !SCIPisLPSolBasic(scip) ) return SCIP_OKAY; /* only call separator, if there are fractional variables */ if( SCIPgetNLPBranchCands(scip) == 0 ) return SCIP_OKAY; /* get variables data */ SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL) ); /* get LP data */ SCIP_CALL( SCIPgetLPColsData(scip, &cols, &ncols) ); SCIP_CALL( SCIPgetLPRowsData(scip, &rows, &nrows) ); if( ncols == 0 || nrows == 0 ) return SCIP_OKAY; #if 0 /* if too many columns, separator is usually very slow: delay it until no other cuts have been found */ if( ncols >= 50*nrows ) return SCIP_OKAY; if( ncols >= 5*nrows ) { int ncutsfound; ncutsfound = SCIPgetNCutsFound(scip); if( ncutsfound > sepadata->lastncutsfound || !SCIPsepaWasLPDelayed(sepa) ) { sepadata->lastncutsfound = ncutsfound; *result = SCIP_DELAYED; return SCIP_OKAY; } } #endif /* get the type of norm to use for efficacy calculations */ SCIP_CALL( SCIPgetCharParam(scip, "separating/efficacynorm", &normtype) ); /* set the maximal denominator in rational representation of strong CG cut and the maximal scale factor to * scale resulting cut to integral values to avoid numerical instabilities */ /**@todo find better but still stable strong CG cut settings: look at dcmulti, gesa3, khb0525, misc06, p2756 */ maxdepth = SCIPgetMaxDepth(scip); if( depth == 0 ) { maxdnom = 1000; maxscale = 1000.0; } else if( depth <= maxdepth/4 ) { maxdnom = 1000; maxscale = 1000.0; } else if( depth <= maxdepth/2 ) { maxdnom = 100; maxscale = 100.0; } else { maxdnom = 10; maxscale = 10.0; } *result = SCIP_DIDNOTFIND; /* allocate temporary memory */ SCIP_CALL( SCIPallocBufferArray(scip, &cutcoefs, nvars) ); SCIP_CALL( SCIPallocBufferArray(scip, &basisind, nrows) ); SCIP_CALL( SCIPallocBufferArray(scip, &binvrow, nrows) ); SCIP_CALL( SCIPallocBufferArray(scip, &inds, nrows) ); varsolvals = NULL; /* allocate this later, if needed */ /* get basis indices */ SCIP_CALL( SCIPgetLPBasisInd(scip, basisind) ); /* get the maximal number of cuts allowed in a separation round */ if( depth == 0 ) maxsepacuts = sepadata->maxsepacutsroot; else maxsepacuts = sepadata->maxsepacuts; SCIPdebugMessage("searching strong CG cuts: %d cols, %d rows, maxdnom=%" SCIP_LONGINT_FORMAT ", maxscale=%g, maxcuts=%d\n", ncols, nrows, maxdnom, maxscale, maxsepacuts); /* for all basic columns belonging to integer variables, try to generate a strong CG cut */ ncuts = 0; for( i = 0; i < nrows && ncuts < maxsepacuts && !SCIPisStopped(scip) && *result != SCIP_CUTOFF; ++i ) { SCIP_Bool tryrow; tryrow = FALSE; c = basisind[i]; if( c >= 0 ) { SCIP_VAR* var; assert(c < ncols); var = SCIPcolGetVar(cols[c]); if( SCIPvarGetType(var) != SCIP_VARTYPE_CONTINUOUS ) { SCIP_Real primsol; primsol = SCIPcolGetPrimsol(cols[c]); assert(SCIPgetVarSol(scip, var) == primsol); /*lint !e777*/ if( SCIPfeasFrac(scip, primsol) >= MINFRAC ) { SCIPdebugMessage("trying strong CG cut for col <%s> [%g]\n", SCIPvarGetName(var), primsol); tryrow = TRUE; } } } #ifdef SEPARATEROWS else { SCIP_ROW* row; assert(0 <= -c-1 && -c-1 < nrows); row = rows[-c-1]; if( SCIProwIsIntegral(row) && !SCIProwIsModifiable(row) ) { SCIP_Real primsol; primsol = SCIPgetRowActivity(scip, row); if( SCIPfeasFrac(scip, primsol) >= MINFRAC ) { SCIPdebugMessage("trying strong CG cut for row <%s> [%g]\n", SCIProwGetName(row), primsol); tryrow = TRUE; } } } #endif if( tryrow ) { /* get the row of B^-1 for this basic integer variable with fractional solution value */ SCIP_CALL( SCIPgetLPBInvRow(scip, i, binvrow, inds, &ninds) ); #ifdef SCIP_DEBUG /* initialize variables, that might not have been initialized in SCIPcalcMIR if success == FALSE */ cutact = 0.0; cutrhs = SCIPinfinity(scip); #endif /* create a strong CG cut out of the weighted LP rows using the B^-1 row as weights */ SCIP_CALL( SCIPcalcStrongCG(scip, BOUNDSWITCH, USEVBDS, ALLOWLOCAL, (int) MAXAGGRLEN(nvars), sepadata->maxweightrange, MINFRAC, MAXFRAC, binvrow, inds, ninds, 1.0, cutcoefs, &cutrhs, &cutact, &success, &cutislocal, &cutrank) ); assert(ALLOWLOCAL || !cutislocal); SCIPdebugMessage(" -> success=%u: %g <= %g\n", success, cutact, cutrhs); /* if successful, convert dense cut into sparse row, and add the row as a cut */ if( success && SCIPisFeasGT(scip, cutact, cutrhs) ) { SCIP_VAR** cutvars; SCIP_Real* cutvals; SCIP_Real cutnorm; int cutlen; /* if this is the first successful cut, get the LP solution for all COLUMN variables */ if( varsolvals == NULL ) { int v; SCIP_CALL( SCIPallocBufferArray(scip, &varsolvals, nvars) ); for( v = 0; v < nvars; ++v ) { if( SCIPvarGetStatus(vars[v]) == SCIP_VARSTATUS_COLUMN ) varsolvals[v] = SCIPvarGetLPSol(vars[v]); } } assert(varsolvals != NULL); /* get temporary memory for storing the cut as sparse row */ SCIP_CALL( SCIPallocBufferArray(scip, &cutvars, nvars) ); SCIP_CALL( SCIPallocBufferArray(scip, &cutvals, nvars) ); /* store the cut as sparse row, calculate activity and norm of cut */ SCIP_CALL( storeCutInArrays(scip, nvars, vars, cutcoefs, varsolvals, normtype, cutvars, cutvals, &cutlen, &cutact, &cutnorm) ); SCIPdebugMessage(" -> strong CG cut for <%s>: act=%f, rhs=%f, norm=%f, eff=%f, rank=%d\n", c >= 0 ? SCIPvarGetName(SCIPcolGetVar(cols[c])) : SCIProwGetName(rows[-c-1]), cutact, cutrhs, cutnorm, (cutact - cutrhs)/cutnorm, cutrank); if( SCIPisPositive(scip, cutnorm) && SCIPisEfficacious(scip, (cutact - cutrhs)/cutnorm) ) { SCIP_ROW* cut; char cutname[SCIP_MAXSTRLEN]; /* create the cut */ if( c >= 0 ) (void) SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "scg%d_x%d", SCIPgetNLPs(scip), c); else (void) SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "scg%d_s%d", SCIPgetNLPs(scip), -c-1); SCIP_CALL( SCIPcreateEmptyRowSepa(scip, &cut, sepa, cutname, -SCIPinfinity(scip), cutrhs, cutislocal, FALSE, sepadata->dynamiccuts) ); SCIP_CALL( SCIPaddVarsToRow(scip, cut, cutlen, cutvars, cutvals) ); /*SCIPdebug( SCIP_CALL(SCIPprintRow(scip, cut, NULL)) );*/ SCIProwChgRank(cut, cutrank); assert(success); #ifdef MAKECUTINTEGRAL /* try to scale the cut to integral values */ SCIP_CALL( SCIPmakeRowIntegral(scip, cut, -SCIPepsilon(scip), SCIPsumepsilon(scip), maxdnom, maxscale, MAKECONTINTEGRAL, &success) ); #else #ifdef MAKEINTCUTINTEGRAL /* try to scale the cut to integral values if there are no continuous variables * -> leads to an integral slack variable that can later be used for other cuts */ { int k = 0; while ( k < cutlen && SCIPvarIsIntegral(cutvars[k]) ) ++k; if( k == cutlen ) { SCIP_CALL( SCIPmakeRowIntegral(scip, cut, -SCIPepsilon(scip), SCIPsumepsilon(scip), maxdnom, maxscale, MAKECONTINTEGRAL, &success) ); } } #endif #endif #ifndef FORCECUTINTEGRAL success = TRUE; #endif if( success ) { if( !SCIPisCutEfficacious(scip, NULL, cut) ) { SCIPdebugMessage(" -> strong CG cut <%s> no longer efficacious: act=%f, rhs=%f, norm=%f, eff=%f\n", cutname, SCIPgetRowLPActivity(scip, cut), SCIProwGetRhs(cut), SCIProwGetNorm(cut), SCIPgetCutEfficacy(scip, NULL, cut)); /*SCIPdebug( SCIP_CALL(SCIPprintRow(scip, cut, NULL)) );*/ success = FALSE; } else { SCIP_Bool infeasible; SCIPdebugMessage(" -> found strong CG cut <%s>: act=%f, rhs=%f, norm=%f, eff=%f, min=%f, max=%f (range=%f)\n", cutname, SCIPgetRowLPActivity(scip, cut), SCIProwGetRhs(cut), SCIProwGetNorm(cut), SCIPgetCutEfficacy(scip, NULL, cut), SCIPgetRowMinCoef(scip, cut), SCIPgetRowMaxCoef(scip, cut), SCIPgetRowMaxCoef(scip, cut)/SCIPgetRowMinCoef(scip, cut)); /*SCIPdebug( SCIP_CALL(SCIPprintRow(scip, cut, NULL)) );*/ SCIP_CALL( SCIPaddCut(scip, NULL, cut, FALSE, &infeasible) ); if ( infeasible ) *result = SCIP_CUTOFF; else { if( !cutislocal ) { SCIP_CALL( SCIPaddPoolCut(scip, cut) ); } *result = SCIP_SEPARATED; } ncuts++; } } else { SCIPdebugMessage(" -> strong CG cut <%s> couldn't be scaled to integral coefficients: act=%f, rhs=%f, norm=%f, eff=%f\n", cutname, cutact, cutrhs, cutnorm, SCIPgetCutEfficacy(scip, NULL, cut)); } /* release the row */ SCIP_CALL( SCIPreleaseRow(scip, &cut) ); } /* free temporary memory */ SCIPfreeBufferArray(scip, &cutvals); SCIPfreeBufferArray(scip, &cutvars); } } } /* free temporary memory */ SCIPfreeBufferArrayNull(scip, &varsolvals); SCIPfreeBufferArray(scip, &inds); SCIPfreeBufferArray(scip, &binvrow); SCIPfreeBufferArray(scip, &basisind); SCIPfreeBufferArray(scip, &cutcoefs); SCIPdebugMessage("end searching strong CG cuts: found %d cuts\n", ncuts); sepadata->lastncutsfound = SCIPgetNCutsFound(scip); return SCIP_OKAY; }