void scip_fix_diff(SCIP* scip, SCIP_CONSHDLR* conshdlr, SCIP_VAR* a, SCIP_VAR* b, llint x) { assert(a != b); assert(a && b && scip && conshdlr); static unsigned int n = 0; char row_id[64]; SCIP_ROW* row; // SCIP_VAR* vars[2] = {a, b}; // SCIP_Real coefs[3] = {1, -1}; sprintf(row_id, "drow%d", n++); sa(SCIPcreateEmptyRowCons(scip, &row, conshdlr, row_id, x, x, TRUE, FALSE, FALSE)); sa(SCIPcacheRowExtensions(scip, row)); sa(SCIPaddVarToRow(scip, row, a, 1.0)); sa(SCIPaddVarToRow(scip, row, b, -1.0)); sa(SCIPflushRowExtensions(scip, row)); sa(SCIPaddCut(scip, NULL, row, TRUE)); cout << "added cut" << endl; }
/** separate */ static SCIP_RETCODE sep_flow( SCIP* scip, /**< SCIP data structure */ SCIP_CONSHDLR* conshdlr, /**< constraint handler */ SCIP_CONSHDLRDATA* conshdlrdata, /**< constraint handler data */ SCIP_CONSDATA* consdata, /**< constraint data */ int maxcuts, /**< maximal number of cuts */ int* ncuts /**< pointer to store number of cuts */ ) { GRAPH* g; SCIP_VAR** vars; SCIP_ROW* row = NULL; SCIP_Real* xval; SCIP_Real sum; int i; int k; int j; int ind; int layer; int count = 0; unsigned int flowsep; assert(scip != NULL); assert(conshdlr != NULL); assert(conshdlrdata != NULL); vars = SCIPprobdataGetVars(scip); flowsep = conshdlrdata->flowsep; /* get the graph */ g = consdata->graph; assert(g != NULL); xval = SCIPprobdataGetXval(scip, NULL); assert(xval != NULL); for(i = 0; i < g->knots; i++) { for(layer = 0; layer < g->layers; layer++) { /* continue at root */ if( i == g->source[layer] ) continue; /* at terminal: input sum == 1 * basically a cut (starcut)) */ if( g->term[i] == layer ) { sum = 0.0; for( k = g->inpbeg[i]; k != EAT_LAST; k = g->ieat[k] ) { ind = layer * g->edges + k; sum += (xval != NULL) ? xval[ind] : 0.0; } if( !SCIPisFeasEQ(scip, sum, 1.0) ) { SCIP_Bool infeasible; SCIP_CALL( SCIPcreateEmptyRowCons(scip, &row, conshdlr, "term", 1.0, 1.0, FALSE, FALSE, TRUE) ); SCIP_CALL( SCIPcacheRowExtensions(scip, row) ); for(k = g->inpbeg[i]; k != EAT_LAST; k = g->ieat[k]) { ind = layer * g->edges + k; SCIP_CALL( SCIPaddVarToRow(scip, row, vars[ind], 1.0) ); } SCIP_CALL( SCIPflushRowExtensions(scip, row) ); SCIP_CALL( SCIPaddCut(scip, NULL, row, FALSE, &infeasible) ); count++; SCIP_CALL( SCIPreleaseRow(scip, &row) ); if( *ncuts + count >= maxcuts ) goto TERMINATE; } } /* no flows ? */ if( !flowsep ) continue; /* the value of each outgoing edge needs to be smaller than the sum of the ingoing edges */ for( j = g->outbeg[i]; j != EAT_LAST; j = g->oeat[j] ) { ind = layer * g->edges + j; sum = (xval != NULL) ? -xval[ind] : -1.0; for( k = g->inpbeg[i]; k != EAT_LAST; k = g->ieat[k] ) { ind = layer * g->edges + k; sum += (xval != NULL) ? xval[ind] : 0.0; } if( SCIPisFeasNegative(scip, sum) ) { SCIP_Bool infeasible; SCIP_CALL( SCIPcreateEmptyRowCons(scip, &row, conshdlr, "flow", 0.0, SCIPinfinity(scip), FALSE, FALSE, TRUE) ); SCIP_CALL( SCIPcacheRowExtensions(scip, row) ); ind = layer * g->edges + j; SCIP_CALL( SCIPaddVarToRow(scip, row, vars[ind], -1.0) ); for( k = g->inpbeg[i]; k != EAT_LAST; k = g->ieat[k] ) { ind = layer * g->edges + k; SCIP_CALL( SCIPaddVarToRow(scip, row, vars[ind], 1.0) ); } SCIP_CALL( SCIPflushRowExtensions(scip, row) ); SCIP_CALL( SCIPaddCut(scip, NULL, row, FALSE, &infeasible) ); count++; SCIP_CALL( SCIPreleaseRow(scip, &row) ); if( *ncuts + count >= maxcuts ) goto TERMINATE; } } /* consider only non terminals */ if( g->term[i] == layer ) continue; /* input of a vertex has to be <= 1.0 */ sum = 0.0; for( k = g->inpbeg[i]; k != EAT_LAST; k = g->ieat[k] ) { ind = layer * g->edges + k; sum += (xval != NULL) ? xval[ind] : 1.0; } if( SCIPisFeasGT(scip, sum, 1.0) ) { SCIP_Bool infeasible; SCIP_CALL( SCIPcreateEmptyRowCons(scip, &row, conshdlr, "infl", -SCIPinfinity(scip), 1.0, FALSE, FALSE, TRUE) ); SCIP_CALL( SCIPcacheRowExtensions(scip, row) ); for( k = g->inpbeg[i]; k != EAT_LAST; k = g->ieat[k] ) { ind = layer * g->edges + k; SCIP_CALL( SCIPaddVarToRow(scip, row, vars[ind], 1.0) ); } SCIP_CALL( SCIPflushRowExtensions(scip, row) ); SCIP_CALL( SCIPaddCut(scip, NULL, row, FALSE, &infeasible) ); count++; SCIP_CALL( SCIPreleaseRow(scip, &row) ); if( *ncuts + count >= maxcuts ) goto TERMINATE; } /* incoming flow <= outgoing flow */ sum = 0.0; for( k = g->inpbeg[i]; k != EAT_LAST; k = g->ieat[k] ) { ind = layer * g->edges + k; sum -= (xval != NULL) ? xval[ind] : 1.0; } for( k = g->outbeg[i]; k != EAT_LAST; k = g->oeat[k] ) { ind = layer * g->edges + k; sum += (xval != NULL) ? xval[ind] : 0.0; } if( SCIPisFeasNegative(scip, sum) ) { SCIP_Bool infeasible; SCIP_CALL( SCIPcreateEmptyRowCons(scip, &row, conshdlr, "bala", 0.0, (g->locals[layer] == 2) ? 0.0 : SCIPinfinity(scip), FALSE, FALSE, TRUE) ); SCIP_CALL( SCIPcacheRowExtensions(scip, row) ); for( k = g->inpbeg[i]; k != EAT_LAST; k = g->ieat[k] ) { ind = layer * g->edges + k; SCIP_CALL( SCIPaddVarToRow(scip, row, vars[ind], -1.0) ); } for( k = g->outbeg[i]; k != EAT_LAST; k = g->oeat[k] ) { ind = layer * g->edges + k; SCIP_CALL( SCIPaddVarToRow(scip, row, vars[ind], 1.0) ); } SCIP_CALL( SCIPflushRowExtensions(scip, row) ); SCIP_CALL( SCIPaddCut(scip, NULL, row, FALSE, &infeasible) ); count++; SCIP_CALL( SCIPreleaseRow(scip, &row) ); if( *ncuts + count >= maxcuts ) goto TERMINATE; } } } TERMINATE: SCIPdebugMessage("In/Out Separator: %d Inequalities added\n", count); *ncuts += count; return SCIP_OKAY; }
/** add a cut */ static SCIP_RETCODE cut_add( SCIP* scip, /**< SCIP data structure */ SCIP_CONSHDLR* conshdlr, /**< constraint handler */ const GRAPH* g, /**< graph data structure */ const int layer, /**< current layer, set to zero usually */ const SCIP_Real* xval, /**< edge values */ int* capa, /**< edges capacities (scaled) */ const int updatecapa, /**< update capacities? */ int* ncuts, /**< pointer to store number of cuts */ SCIP_Bool* success /**< pointer to store whether add cut be added */ ) { SCIP_ROW* row; SCIP_VAR** vars = SCIPprobdataGetVars(scip); SCIP_Real sum = 0.0; SCIP_Bool inccapa = FALSE; int i; int ind; (*success) = FALSE; assert(scip != NULL); assert(g != NULL); assert((layer >= 0) && (layer < g->layers)); SCIP_CALL( SCIPcreateEmptyRowCons(scip, &row, conshdlr, "2cut", 1.0, SCIPinfinity(scip), FALSE, FALSE, TRUE) ); SCIP_CALL( SCIPcacheRowExtensions(scip, row) ); for( i = 0; i < g->edges; i++ ) { if( (g->mark[g->source[layer]] == g->mark[g->tail[i]]) && (g->mark[g->tail[i]] != g->mark[g->head[i]]) ) { ind = layer * g->edges + i; if( updatecapa ) { if( capa[i] < FLOW_FACTOR ) inccapa = TRUE; SCIPdebugMessage("set capa[%d] from %6d to %6d\n", i, capa[i], FLOW_FACTOR); capa[i] = FLOW_FACTOR; if( !inccapa ) { SCIP_CALL( SCIPflushRowExtensions(scip, row) ); SCIP_CALL( SCIPreleaseRow(scip, &row) ); return SCIP_OKAY; } } if( xval != NULL ) { sum += xval[ind]; if( SCIPisFeasGE(scip, sum, 1.0) ) { SCIP_CALL( SCIPflushRowExtensions(scip, row) ); SCIP_CALL( SCIPreleaseRow(scip, &row) ); return SCIP_OKAY; } } SCIP_CALL( SCIPaddVarToRow(scip, row, vars[ind], 1.0) ); } } assert(sum < 1.0); SCIP_CALL( SCIPflushRowExtensions(scip, row) ); /* checks, if cut is sufficiently violated */ if( SCIPisCutEfficacious(scip, NULL, row) ) { SCIP_Bool infeasible; SCIPdebug( SCIP_CALL( SCIPprintRow(scip, row, NULL) ) ); SCIP_CALL( SCIPaddCut(scip, NULL, row, FALSE, &infeasible) ); (*ncuts)++; (*success) = TRUE; } SCIP_CALL( SCIPreleaseRow(scip, &row) ); return SCIP_OKAY; }
/** creates the objective value inequality and the objective value variable, if not yet existing */ static SCIP_RETCODE createObjRow( SCIP* scip, /**< SCIP data structure */ SCIP_SEPA* sepa, /**< separator */ SCIP_SEPADATA* sepadata /**< separator data */ ) { assert(sepadata != NULL); if( sepadata->objrow == NULL ) { SCIP_VAR** vars; SCIP_Real obj; SCIP_Real intobjval; int nvars; int v; SCIP_Bool attendobjvarbound; attendobjvarbound = FALSE; /* create and add objective value variable */ if( sepadata->objvar == NULL ) { SCIP_CALL( SCIPcreateVar(scip, &sepadata->objvar, "objvar", -SCIPinfinity(scip), SCIPinfinity(scip), 0.0, SCIP_VARTYPE_IMPLINT, FALSE, TRUE, NULL, NULL, NULL, NULL, NULL) ); SCIP_CALL( SCIPaddVar(scip, sepadata->objvar) ); SCIP_CALL( SCIPaddVarLocks(scip, sepadata->objvar, +1, +1) ); } else attendobjvarbound = TRUE; /* get problem variables */ vars = SCIPgetOrigVars(scip); nvars = SCIPgetNOrigVars(scip); /* create objective value inequality */ if( SCIPgetObjsense(scip) == SCIP_OBJSENSE_MINIMIZE ) { if( attendobjvarbound ) intobjval = SCIPceil(scip, SCIPgetDualbound(scip)) - SCIPvarGetLbGlobal(sepadata->objvar); else intobjval = SCIPceil(scip, SCIPgetDualbound(scip)); SCIP_CALL( SCIPcreateEmptyRowSepa(scip, &sepadata->objrow, sepa, "objrow", intobjval, SCIPinfinity(scip), FALSE, !SCIPallVarsInProb(scip), TRUE) ); sepadata->setoff = intobjval; } else { if( attendobjvarbound ) intobjval = SCIPceil(scip, SCIPgetDualbound(scip)) - SCIPvarGetUbGlobal(sepadata->objvar); else intobjval = SCIPfloor(scip, SCIPgetDualbound(scip)); SCIP_CALL( SCIPcreateEmptyRowSepa(scip, &sepadata->objrow, sepa, "objrow", -SCIPinfinity(scip), intobjval, FALSE, !SCIPallVarsInProb(scip), TRUE) ); sepadata->setoff = intobjval; } SCIP_CALL( SCIPcacheRowExtensions(scip, sepadata->objrow) ); for( v = 0; v < nvars; ++v ) { obj = SCIPvarGetObj(vars[v]); if( !SCIPisZero(scip, obj) ) { SCIP_CALL( SCIPaddVarToRow(scip, sepadata->objrow, vars[v], obj) ); } } SCIP_CALL( SCIPaddVarToRow(scip, sepadata->objrow, sepadata->objvar, -1.0) ); SCIP_CALL( SCIPflushRowExtensions(scip, sepadata->objrow) ); SCIPdebugMessage("created objective value row: "); SCIPdebug( SCIP_CALL( SCIPprintRow(scip, sepadata->objrow, NULL) ) ); } return SCIP_OKAY; }
/** LP solution separation method of separator */ static SCIP_DECL_SEPAEXECLP(sepaExeclpGomory) { /*lint --e{715}*/ SCIP_SEPADATA* sepadata; SCIP_VAR** vars; SCIP_COL** cols; SCIP_ROW** rows; SCIP_Real* binvrow; SCIP_Real* cutcoefs; SCIP_Real maxscale; SCIP_Real minfrac; SCIP_Real maxfrac; SCIP_Longint maxdnom; SCIP_Bool cutoff; int* basisind; int naddedcuts; int nvars; int ncols; int nrows; int ncalls; int depth; int maxdepth; int maxsepacuts; int c; int i; assert(sepa != NULL); assert(strcmp(SCIPsepaGetName(sepa), SEPA_NAME) == 0); assert(scip != NULL); assert(result != NULL); *result = SCIP_DIDNOTRUN; sepadata = SCIPsepaGetData(sepa); assert(sepadata != NULL); depth = SCIPgetDepth(scip); ncalls = SCIPsepaGetNCallsAtNode(sepa); minfrac = sepadata->away; maxfrac = 1.0 - sepadata->away; /* only call separator, if we are not close to terminating */ if( SCIPisStopped(scip) ) return SCIP_OKAY; /* only call the gomory cut separator a given number of times at each node */ if( (depth == 0 && sepadata->maxroundsroot >= 0 && ncalls >= sepadata->maxroundsroot) || (depth > 0 && sepadata->maxrounds >= 0 && ncalls >= sepadata->maxrounds) ) return SCIP_OKAY; /* only call separator, if an optimal LP solution is at hand */ if( SCIPgetLPSolstat(scip) != SCIP_LPSOLSTAT_OPTIMAL ) return SCIP_OKAY; /* only call separator, if the LP solution is basic */ if( !SCIPisLPSolBasic(scip) ) return SCIP_OKAY; /* only call separator, if there are fractional variables */ if( SCIPgetNLPBranchCands(scip) == 0 ) return SCIP_OKAY; /* get variables data */ SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL) ); /* get LP data */ SCIP_CALL( SCIPgetLPColsData(scip, &cols, &ncols) ); SCIP_CALL( SCIPgetLPRowsData(scip, &rows, &nrows) ); if( ncols == 0 || nrows == 0 ) return SCIP_OKAY; #if 0 /* if too many columns, separator is usually very slow: delay it until no other cuts have been found */ if( ncols >= 50*nrows ) return SCIP_OKAY; if( ncols >= 5*nrows ) { int ncutsfound; ncutsfound = SCIPgetNCutsFound(scip); if( ncutsfound > sepadata->lastncutsfound || !SCIPsepaWasLPDelayed(sepa) ) { sepadata->lastncutsfound = ncutsfound; *result = SCIP_DELAYED; return SCIP_OKAY; } } #endif /* set the maximal denominator in rational representation of gomory cut and the maximal scale factor to * scale resulting cut to integral values to avoid numerical instabilities */ /**@todo find better but still stable gomory cut settings: look at dcmulti, gesa3, khb0525, misc06, p2756 */ maxdepth = SCIPgetMaxDepth(scip); if( depth == 0 ) { maxdnom = 1000; maxscale = 1000.0; } else if( depth <= maxdepth/4 ) { maxdnom = 1000; maxscale = 1000.0; } else if( depth <= maxdepth/2 ) { maxdnom = 100; maxscale = 100.0; } else { maxdnom = 10; maxscale = 10.0; } /* allocate temporary memory */ SCIP_CALL( SCIPallocBufferArray(scip, &cutcoefs, nvars) ); SCIP_CALL( SCIPallocBufferArray(scip, &basisind, nrows) ); SCIP_CALL( SCIPallocBufferArray(scip, &binvrow, nrows) ); /* get basis indices */ SCIP_CALL( SCIPgetLPBasisInd(scip, basisind) ); /* get the maximal number of cuts allowed in a separation round */ if( depth == 0 ) maxsepacuts = sepadata->maxsepacutsroot; else maxsepacuts = sepadata->maxsepacuts; SCIPdebugMessage("searching gomory cuts: %d cols, %d rows, maxdnom=%"SCIP_LONGINT_FORMAT", maxscale=%g, maxcuts=%d\n", ncols, nrows, maxdnom, maxscale, maxsepacuts); cutoff = FALSE; naddedcuts = 0; /* for all basic columns belonging to integer variables, try to generate a gomory cut */ for( i = 0; i < nrows && naddedcuts < maxsepacuts && !SCIPisStopped(scip) && !cutoff; ++i ) { SCIP_Bool tryrow; tryrow = FALSE; c = basisind[i]; if( c >= 0 ) { SCIP_VAR* var; assert(c < ncols); var = SCIPcolGetVar(cols[c]); if( SCIPvarGetType(var) != SCIP_VARTYPE_CONTINUOUS ) { SCIP_Real primsol; primsol = SCIPcolGetPrimsol(cols[c]); assert(SCIPgetVarSol(scip, var) == primsol); /*lint !e777*/ if( SCIPfeasFrac(scip, primsol) >= minfrac ) { SCIPdebugMessage("trying gomory cut for col <%s> [%g]\n", SCIPvarGetName(var), primsol); tryrow = TRUE; } } } else if( sepadata->separaterows ) { SCIP_ROW* row; assert(0 <= -c-1 && -c-1 < nrows); row = rows[-c-1]; if( SCIProwIsIntegral(row) && !SCIProwIsModifiable(row) ) { SCIP_Real primsol; primsol = SCIPgetRowActivity(scip, row); if( SCIPfeasFrac(scip, primsol) >= minfrac ) { SCIPdebugMessage("trying gomory cut for row <%s> [%g]\n", SCIProwGetName(row), primsol); tryrow = TRUE; } } } if( tryrow ) { SCIP_Real cutrhs; SCIP_Real cutact; SCIP_Bool success; SCIP_Bool cutislocal; /* get the row of B^-1 for this basic integer variable with fractional solution value */ SCIP_CALL( SCIPgetLPBInvRow(scip, i, binvrow) ); cutact = 0.0; cutrhs = SCIPinfinity(scip); /* create a MIR cut out of the weighted LP rows using the B^-1 row as weights */ SCIP_CALL( SCIPcalcMIR(scip, NULL, BOUNDSWITCH, USEVBDS, ALLOWLOCAL, FIXINTEGRALRHS, NULL, NULL, (int) MAXAGGRLEN(nvars), sepadata->maxweightrange, minfrac, maxfrac, binvrow, 1.0, NULL, NULL, cutcoefs, &cutrhs, &cutact, &success, &cutislocal) ); assert(ALLOWLOCAL || !cutislocal); /* @todo Currently we are using the SCIPcalcMIR() function to compute the coefficients of the Gomory * cut. Alternatively, we could use the direct version (see thesis of Achterberg formula (8.4)) which * leads to cut a of the form \sum a_i x_i \geq 1. Rumor has it that these cuts are better. */ SCIPdebugMessage(" -> success=%u: %g <= %g\n", success, cutact, cutrhs); /* if successful, convert dense cut into sparse row, and add the row as a cut */ if( success && SCIPisFeasGT(scip, cutact, cutrhs) ) { SCIP_ROW* cut; char cutname[SCIP_MAXSTRLEN]; int v; /* construct cut name */ if( c >= 0 ) (void) SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "gom%d_x%d", SCIPgetNLPs(scip), c); else (void) SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "gom%d_s%d", SCIPgetNLPs(scip), -c-1); /* create empty cut */ SCIP_CALL( SCIPcreateEmptyRowSepa(scip, &cut, sepa, cutname, -SCIPinfinity(scip), cutrhs, cutislocal, FALSE, sepadata->dynamiccuts) ); /* cache the row extension and only flush them if the cut gets added */ SCIP_CALL( SCIPcacheRowExtensions(scip, cut) ); /* collect all non-zero coefficients */ for( v = 0; v < nvars; ++v ) { if( !SCIPisZero(scip, cutcoefs[v]) ) { SCIP_CALL( SCIPaddVarToRow(scip, cut, vars[v], cutcoefs[v]) ); } } if( SCIProwGetNNonz(cut) == 0 ) { assert(SCIPisFeasNegative(scip, cutrhs)); SCIPdebugMessage(" -> gomory cut detected infeasibility with cut 0 <= %f\n", cutrhs); cutoff = TRUE; } else if( SCIProwGetNNonz(cut) == 1 ) { /* add the bound change as cut to avoid that the LP gets modified. that would mean the LP is not flushed * and the method SCIPgetLPBInvRow() fails; SCIP internally will apply that bound change automatically */ SCIP_CALL( SCIPaddCut(scip, NULL, cut, TRUE) ); naddedcuts++; } else { /* Only take efficacious cuts, except for cuts with one non-zero coefficients (= bound * changes); the latter cuts will be handeled internally in sepastore. */ if( SCIPisCutEfficacious(scip, NULL, cut) ) { assert(success == TRUE); SCIPdebugMessage(" -> gomory cut for <%s>: act=%f, rhs=%f, eff=%f\n", c >= 0 ? SCIPvarGetName(SCIPcolGetVar(cols[c])) : SCIProwGetName(rows[-c-1]), cutact, cutrhs, SCIPgetCutEfficacy(scip, NULL, cut)); if( sepadata->makeintegral ) { /* try to scale the cut to integral values */ SCIP_CALL( SCIPmakeRowIntegral(scip, cut, -SCIPepsilon(scip), SCIPsumepsilon(scip), maxdnom, maxscale, MAKECONTINTEGRAL, &success) ); if( sepadata->forcecuts ) success = TRUE; /* in case the left hand side in minus infinity and the right hand side is plus infinity the cut is * useless so we are not taking it at all */ if( (SCIPisInfinity(scip, -SCIProwGetLhs(cut)) && SCIPisInfinity(scip, SCIProwGetRhs(cut))) ) success = FALSE; /* @todo Trying to make the Gomory cut integral might fail. Due to numerical reasons/arguments we * currently ignore such cuts. If the cut, however, has small support (let's say smaller or equal to * 5), we might want to add that cut (even it does not have integral coefficients). To be able to * do that we need to add a rank to the data structure of a row. The rank of original rows are * zero and for aggregated rows it is the maximum over all used rows plus one. */ } if( success ) { SCIPdebugMessage(" -> found gomory cut <%s>: act=%f, rhs=%f, norm=%f, eff=%f, min=%f, max=%f (range=%f)\n", cutname, SCIPgetRowLPActivity(scip, cut), SCIProwGetRhs(cut), SCIProwGetNorm(cut), SCIPgetCutEfficacy(scip, NULL, cut), SCIPgetRowMinCoef(scip, cut), SCIPgetRowMaxCoef(scip, cut), SCIPgetRowMaxCoef(scip, cut)/SCIPgetRowMinCoef(scip, cut)); /* flush all changes before adding the cut */ SCIP_CALL( SCIPflushRowExtensions(scip, cut) ); /* add global cuts which are not implicit bound changes to the cut pool */ if( !cutislocal ) { if( sepadata->delayedcuts ) { SCIP_CALL( SCIPaddDelayedPoolCut(scip, cut) ); } else { SCIP_CALL( SCIPaddPoolCut(scip, cut) ); } } else { /* local cuts we add to the sepastore */ SCIP_CALL( SCIPaddCut(scip, NULL, cut, FALSE) ); } naddedcuts++; } } } /* release the row */ SCIP_CALL( SCIPreleaseRow(scip, &cut) ); } } } /* free temporary memory */ SCIPfreeBufferArray(scip, &binvrow); SCIPfreeBufferArray(scip, &basisind); SCIPfreeBufferArray(scip, &cutcoefs); SCIPdebugMessage("end searching gomory cuts: found %d cuts\n", naddedcuts); sepadata->lastncutsfound = SCIPgetNCutsFound(scip); /* evalute the result of the separation */ if( cutoff ) *result = SCIP_CUTOFF; else if ( naddedcuts > 0 ) *result = SCIP_SEPARATED; else *result = SCIP_DIDNOTFIND; return SCIP_OKAY; }
/** computes a disjunctive cut inequality based on two simplex taubleau rows */ static SCIP_RETCODE generateDisjCutSOS1( SCIP* scip, /**< SCIP pointer */ SCIP_SEPA* sepa, /**< separator */ SCIP_ROW** rows, /**< LP rows */ int nrows, /**< number of LP rows */ SCIP_COL** cols, /**< LP columns */ int ncols, /**< number of LP columns */ int ndisjcuts, /**< number of disjunctive cuts found so far */ SCIP_Bool scale, /**< should cut be scaled */ SCIP_Bool strengthen, /**< should cut be strengthened if integer variables are present */ SCIP_Real cutlhs1, /**< left hand side of the first simplex row */ SCIP_Real cutlhs2, /**< left hand side of the second simplex row */ SCIP_Real bound1, /**< bound of first simplex row */ SCIP_Real bound2, /**< bound of second simplex row */ SCIP_Real* simplexcoefs1, /**< simplex coefficients of first row */ SCIP_Real* simplexcoefs2, /**< simplex coefficients of second row */ SCIP_Real* cutcoefs, /**< pointer to store cut coefficients (length: nscipvars) */ SCIP_ROW** row, /**< pointer to store disjunctive cut inequality */ SCIP_Bool* madeintegral /**< pointer to store whether cut has been scaled to integral values */ ) { char cutname[SCIP_MAXSTRLEN]; SCIP_COL** rowcols; SCIP_COL* col; SCIP_Real* rowvals; SCIP_Real lhsrow; SCIP_Real rhsrow; SCIP_Real cutlhs; SCIP_Real sgn; SCIP_Real lb; SCIP_Real ub; int nonbasicnumber = 0; int rownnonz; int ind; int r; int c; assert( scip != NULL ); assert( row != NULL ); assert( rows != NULL ); assert( cols != NULL ); assert( simplexcoefs1 != NULL ); assert( simplexcoefs2 != NULL ); assert( cutcoefs != NULL ); assert( sepa != NULL ); assert( madeintegral != NULL ); *madeintegral = FALSE; /* check signs */ if ( SCIPisFeasPositive(scip, cutlhs1) == SCIPisFeasPositive(scip, cutlhs2) ) sgn = 1.0; else sgn = -1.0; /* check bounds */ if ( SCIPisInfinity(scip, REALABS(bound1)) || SCIPisInfinity(scip, REALABS(bound2)) ) strengthen = FALSE; /* compute left hand side of row (a later update is possible, see below) */ cutlhs = sgn * cutlhs1 * cutlhs2; /* add cut-coefficients of the non-basic non-slack variables */ for (c = 0; c < ncols; ++c) { col = cols[c]; assert( col != NULL ); ind = SCIPcolGetLPPos(col); assert( ind >= 0 ); if ( SCIPcolGetBasisStatus(col) == SCIP_BASESTAT_LOWER ) { lb = SCIPcolGetLb(col); /* for integer variables we may obtain stronger coefficients */ if ( strengthen && SCIPcolIsIntegral(col) ) { SCIP_Real mval; SCIP_Real mvalfloor; SCIP_Real mvalceil; mval = (cutlhs2 * simplexcoefs1[nonbasicnumber] - cutlhs1 * simplexcoefs2[nonbasicnumber]) / (cutlhs2 * bound1 + cutlhs1 * bound2); mvalfloor = SCIPfloor(scip, mval); mvalceil = SCIPceil(scip, mval); cutcoefs[ind] = MIN(sgn * cutlhs2 * (simplexcoefs1[nonbasicnumber] - mvalfloor * bound1), sgn * cutlhs1 * (simplexcoefs2[nonbasicnumber] + mvalceil * bound2)); assert( SCIPisFeasLE(scip, cutcoefs[ind], MAX(sgn * cutlhs2 * simplexcoefs1[nonbasicnumber], sgn * cutlhs1 * simplexcoefs2[nonbasicnumber])) ); } else cutcoefs[ind] = MAX(sgn * cutlhs2 * simplexcoefs1[nonbasicnumber], sgn * cutlhs1 * simplexcoefs2[nonbasicnumber]); cutlhs += cutcoefs[ind] * lb; ++nonbasicnumber; } else if ( SCIPcolGetBasisStatus(col) == SCIP_BASESTAT_UPPER ) { ub = SCIPcolGetUb(col); /* for integer variables we may obtain stronger coefficients */ if ( strengthen && SCIPcolIsIntegral(col) ) { SCIP_Real mval; SCIP_Real mvalfloor; SCIP_Real mvalceil; mval = (cutlhs2 * simplexcoefs1[nonbasicnumber] - cutlhs1 * simplexcoefs2[nonbasicnumber]) / (cutlhs2 * bound1 + cutlhs1 * bound2); mvalfloor = SCIPfloor(scip, -mval); mvalceil = SCIPceil(scip, -mval); cutcoefs[ind] = MAX(sgn * cutlhs2 * (simplexcoefs1[nonbasicnumber] + mvalfloor * bound1), sgn * cutlhs1 * (simplexcoefs2[nonbasicnumber] - mvalceil * bound2)); assert( SCIPisFeasLE(scip, -cutcoefs[ind], -MIN(sgn * cutlhs2 * simplexcoefs1[nonbasicnumber], sgn * cutlhs1 * simplexcoefs2[nonbasicnumber])) ); } else cutcoefs[ind] = MIN(sgn * cutlhs2 * simplexcoefs1[nonbasicnumber], sgn * cutlhs1 * simplexcoefs2[nonbasicnumber]); cutlhs += cutcoefs[ind] * ub; ++nonbasicnumber; } else { assert( SCIPcolGetBasisStatus(col) != SCIP_BASESTAT_ZERO ); cutcoefs[ind] = 0.0; } } /* add cut-coefficients of the non-basic slack variables */ for (r = 0; r < nrows; ++r) { rhsrow = SCIProwGetRhs(rows[r]) - SCIProwGetConstant(rows[r]); lhsrow = SCIProwGetLhs(rows[r]) - SCIProwGetConstant(rows[r]); assert( SCIProwGetBasisStatus(rows[r]) != SCIP_BASESTAT_ZERO ); assert( SCIPisFeasZero(scip, lhsrow - rhsrow) || SCIPisNegative(scip, lhsrow - rhsrow) ); assert( SCIProwIsInLP(rows[r]) ); if ( SCIProwGetBasisStatus(rows[r]) != SCIP_BASESTAT_BASIC ) { SCIP_Real cutcoef; if ( SCIProwGetBasisStatus(rows[r]) == SCIP_BASESTAT_UPPER ) { assert( SCIPisFeasZero(scip, SCIPgetRowLPActivity(scip, rows[r]) - SCIProwGetRhs(rows[r])) ); cutcoef = MAX(sgn * cutlhs2 * simplexcoefs1[nonbasicnumber], sgn * cutlhs1 * simplexcoefs2[nonbasicnumber]); cutlhs -= cutcoef * rhsrow; ++nonbasicnumber; } else /* SCIProwGetBasisStatus(rows[r]) == SCIP_BASESTAT_LOWER */ { assert( SCIProwGetBasisStatus(rows[r]) == SCIP_BASESTAT_LOWER ); assert( SCIPisFeasZero(scip, SCIPgetRowLPActivity(scip, rows[r]) - SCIProwGetLhs(rows[r])) ); cutcoef = MIN(sgn * cutlhs2 * simplexcoefs1[nonbasicnumber], sgn * cutlhs1 * simplexcoefs2[nonbasicnumber]); cutlhs -= cutcoef * lhsrow; ++nonbasicnumber; } rownnonz = SCIProwGetNNonz(rows[r]); rowvals = SCIProwGetVals(rows[r]); rowcols = SCIProwGetCols(rows[r]); for (c = 0; c < rownnonz; ++c) { ind = SCIPcolGetLPPos(rowcols[c]); /* if column is not in LP, then return without generating cut */ if ( ind < 0 ) { *row = NULL; return SCIP_OKAY; } cutcoefs[ind] -= cutcoef * rowvals[c]; } } } /* create cut */ (void) SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "%s_%d_%d", SCIPsepaGetName(sepa), SCIPgetNLPs(scip), ndisjcuts); if ( SCIPgetDepth(scip) == 0 ) SCIP_CALL( SCIPcreateEmptyRowSepa(scip, row, sepa, cutname, cutlhs, SCIPinfinity(scip), FALSE, FALSE, TRUE) ); else SCIP_CALL( SCIPcreateEmptyRowSepa(scip, row, sepa, cutname, cutlhs, SCIPinfinity(scip), TRUE, FALSE, TRUE) ); SCIP_CALL( SCIPcacheRowExtensions(scip, *row) ); for (c = 0; c < ncols; ++c) { ind = SCIPcolGetLPPos(cols[c]); assert( ind >= 0 ); if ( ! SCIPisFeasZero(scip, cutcoefs[ind]) ) { SCIP_CALL( SCIPaddVarToRow(scip, *row, SCIPcolGetVar(cols[c]), cutcoefs[ind] ) ); } } SCIP_CALL( SCIPflushRowExtensions(scip, *row) ); /* try to scale the cut to integral values * @todo find better but still stable disjunctive cut settings */ if ( scale ) { int maxdepth; int depth; SCIP_Longint maxdnom; SCIP_Real maxscale; depth = SCIPgetDepth(scip); assert( depth >= 0 ); maxdepth = SCIPgetMaxDepth(scip); if ( depth == 0 ) { maxdnom = 1000; maxscale = 1000.0; } else if ( depth <= maxdepth/4 ) { maxdnom = 1000; maxscale = 1000.0; } else if ( depth <= maxdepth/2 ) { maxdnom = 100; maxscale = 100.0; } else { maxdnom = 10; maxscale = 10.0; } SCIP_CALL( SCIPmakeRowIntegral(scip, *row, -SCIPepsilon(scip), SCIPsumepsilon(scip), maxdnom, maxscale, TRUE, madeintegral) ); } return SCIP_OKAY; }
SCIP_RETCODE SCIPconshdlrBenders::sepaBenders( SCIP * scip, SCIP_CONSHDLR * conshdlr, SCIP_SOL * sol, whereFrom where, SCIP_RESULT * result) { OsiCuts cs; /**< Benders cut placeholder */ SCIP_Real * vals = NULL; /**< current solution */ #if 1 if (scip_checkpriority_ < 0) { /** consider incumbent solutions only */ double primObj = SCIPgetPrimalbound(scip); double currObj = SCIPgetSolOrigObj(scip, sol); if (SCIPisLT(scip, primObj, currObj)) { DSPdebugMessage(" -> primObj %e currObj %e\n", primObj, currObj); return SCIP_OKAY; } } #endif /** allocate memory */ SCIP_CALL(SCIPallocMemoryArray(scip, &vals, nvars_)); /** get current solution */ SCIP_CALL(SCIPgetSolVals(scip, sol, nvars_, vars_, vals)); /** TODO The following filter does not work, meaning that it provides suboptimal solution. * I do not know the reason. */ #if 0 double maxviol = 1.e-10; for (int j = 0; j < nvars_ - naux_; ++j) { SCIP_VARTYPE vartype = SCIPvarGetType(vars_[j]); if (vartype == SCIP_VARTYPE_CONTINUOUS) continue; double viol = 0.5 - fabs(vals[j] - floor(vals[j]) - 0.5); if (viol > maxviol) maxviol = viol; } DSPdebugMessage("maximum violation %e\n", maxviol); if (where != from_scip_check && where != from_scip_enfolp && where != from_scip_enfops && maxviol > 1.e-7) { printf("where %d maxviol %e\n", where, maxviol); /** free memory */ SCIPfreeMemoryArray(scip, &vals); return SCIP_OKAY; } #endif #ifdef DSP_DEBUG2 double minvals = COIN_DBL_MAX; double maxvals = -COIN_DBL_MAX; double sumvals = 0.; double ssvals = 0.; //printf("nvars_ %d naux_ %d nAuxvars_ %d\n", nvars_, naux_, tss_->nAuxvars_); for (int j = 0; j < nvars_ - naux_; ++j) { // if (vals[j] < 0 || vals[j] > 1) // printf("solution %d has value %e.\n", j, vals[j]); sumvals += vals[j]; ssvals += vals[j] * vals[j]; minvals = minvals > vals[j] ? vals[j] : minvals; maxvals = maxvals < vals[j] ? vals[j] : maxvals; } DSPdebugMessage("solution: min %e max %e avg %e sum %e two-norm %e\n", minvals, maxvals, sumvals / nvars_, sumvals, sqrt(ssvals)); #endif #define SCAN_GLOBAL_CUT_POOL #ifdef SCAN_GLOBAL_CUT_POOL if (SCIPgetStage(scip) == SCIP_STAGE_SOLVING || SCIPgetStage(scip) == SCIP_STAGE_SOLVED || SCIPgetStage(scip) == SCIP_STAGE_EXITSOLVE) { bool addedPoolCut = false; int numPoolCuts = SCIPgetNPoolCuts(scip); int numCutsToScan = 100; SCIP_CUT ** poolcuts = SCIPgetPoolCuts(scip); for (int i = numPoolCuts - 1; i >= 0; --i) { if (i < 0) break; if (numCutsToScan == 0) break; /** retrieve row */ SCIP_ROW * poolcutrow = SCIPcutGetRow(poolcuts[i]); /** benders? */ if (strcmp(SCIProwGetName(poolcutrow), "benders") != 0) continue; /** counter */ numCutsToScan--; if (SCIPgetCutEfficacy(scip, sol, poolcutrow) > 1.e-6) { if (where == from_scip_sepalp || where == from_scip_sepasol || where == from_scip_enfolp) { /** add cut */ SCIP_Bool infeasible; SCIP_CALL(SCIPaddCut(scip, sol, poolcutrow, FALSE, /**< force cut */ &infeasible)); if (infeasible) *result = SCIP_CUTOFF; else //if (*result != SCIP_CUTOFF) *result = SCIP_SEPARATED; } else *result = SCIP_INFEASIBLE; addedPoolCut = true; break; } } if (addedPoolCut) { DSPdebugMessage("Added pool cut\n"); /** free memory */ SCIPfreeMemoryArray(scip, &vals); return SCIP_OKAY; } } #endif /** generate Benders cuts */ assert(tss_); tss_->generateCuts(nvars_, vals, &cs); /** If found Benders cuts */ for (int i = 0; i < cs.sizeCuts(); ++i) { /** get cut pointer */ OsiRowCut * rc = cs.rowCutPtr(i); if (!rc) continue; const CoinPackedVector cutrow = rc->row(); if (cutrow.getNumElements() == 0) continue; /** is optimality cut? */ bool isOptimalityCut = false; for (int j = nvars_ - naux_; j < nvars_; ++j) { if (cutrow.getMaxIndex() == j) { isOptimalityCut = true; break; } } double efficacy = rc->violated(vals) / cutrow.twoNorm(); SCIP_Bool isEfficacious = efficacy > 1.e-6; #define KK_TEST #ifdef KK_TEST if (SCIPgetStage(scip) == SCIP_STAGE_INITSOLVE || SCIPgetStage(scip) == SCIP_STAGE_SOLVING) { /** create empty row */ SCIP_ROW * row = NULL; SCIP_CALL(SCIPcreateEmptyRowCons(scip, &row, conshdlr, "benders", rc->lb(), SCIPinfinity(scip), FALSE, /**< is row local? */ FALSE, /**< is row modifiable? */ FALSE /**< is row removable? can this be TRUE? */)); /** cache the row extension and only flush them if the cut gets added */ SCIP_CALL(SCIPcacheRowExtensions(scip, row)); /** collect all non-zero coefficients */ for (int j = 0; j < cutrow.getNumElements(); ++j) SCIP_CALL(SCIPaddVarToRow(scip, row, vars_[cutrow.getIndices()[j]], cutrow.getElements()[j])); DSPdebugMessage("found Benders (%s) cut: act=%f, lhs=%f, norm=%f, eff=%f, min=%f, max=%f (range=%f)\n", isOptimalityCut ? "opti" : "feas", SCIPgetRowLPActivity(scip, row), SCIProwGetLhs(row), SCIProwGetNorm(row), SCIPgetCutEfficacy(scip, sol, row), SCIPgetRowMinCoef(scip, row), SCIPgetRowMaxCoef(scip, row), SCIPgetRowMaxCoef(scip, row)/SCIPgetRowMinCoef(scip, row)); /** flush all changes before adding cut */ SCIP_CALL(SCIPflushRowExtensions(scip, row)); DSPdebugMessage("efficacy %e isEfficatious %d\n", efficacy, isEfficacious); if (isEfficacious) { if (where == from_scip_sepalp || where == from_scip_sepasol || where == from_scip_enfolp) { /** add cut */ SCIP_Bool infeasible; SCIP_CALL(SCIPaddCut(scip, sol, row, FALSE, /**< force cut */ &infeasible)); if (infeasible) *result = SCIP_CUTOFF; else //if (*result != SCIP_CUTOFF) *result = SCIP_SEPARATED; } else *result = SCIP_INFEASIBLE; } /** add cut to global pool */ SCIP_CALL(SCIPaddPoolCut(scip, row)); DSPdebugMessage("number of cuts in global cut pool: %d\n", SCIPgetNPoolCuts(scip)); /** release the row */ SCIP_CALL(SCIPreleaseRow(scip, &row)); } else if (isEfficacious && where != from_scip_sepalp && where != from_scip_sepasol && where != from_scip_enfolp) *result = SCIP_INFEASIBLE; #else if (where == from_scip_sepalp || where == from_scip_sepasol || where == from_scip_enfolp) { /** create empty row */ SCIP_ROW * row = NULL; SCIP_CALL(SCIPcreateEmptyRowCons(scip, &row, conshdlr, "benders", rc->lb(), SCIPinfinity(scip), FALSE, /**< is row local? */ FALSE, /**< is row modifiable? */ FALSE /**< is row removable? can this be TRUE? */)); /** cache the row extension and only flush them if the cut gets added */ SCIP_CALL(SCIPcacheRowExtensions(scip, row)); /** collect all non-zero coefficients */ for (int j = 0; j < cutrow.getNumElements(); ++j) SCIP_CALL(SCIPaddVarToRow(scip, row, vars_[cutrow.getIndices()[j]], cutrow.getElements()[j])); DSPdebugMessage("found Benders (%s) cut: act=%f, lhs=%f, norm=%f, eff=%f, min=%f, max=%f (range=%f)\n", isOptimalityCut ? "opti" : "feas", SCIPgetRowLPActivity(scip, row), SCIProwGetLhs(row), SCIProwGetNorm(row), SCIPgetCutEfficacy(scip, NULL, row), SCIPgetRowMinCoef(scip, row), SCIPgetRowMaxCoef(scip, row), SCIPgetRowMaxCoef(scip, row)/SCIPgetRowMinCoef(scip, row)); /** flush all changes before adding cut */ SCIP_CALL(SCIPflushRowExtensions(scip, row)); /** is cut efficacious? */ if (isOptimalityCut) { efficacy = SCIPgetCutEfficacy(scip, sol, row); isEfficacious = SCIPisCutEfficacious(scip, sol, row); } else { efficacy = rc->violated(vals); isEfficacious = efficacy > 1.e-6; } if (isEfficacious) { /** add cut */ SCIP_Bool infeasible; SCIP_CALL(SCIPaddCut(scip, sol, row, FALSE, /**< force cut */ &infeasible)); if (infeasible) *result = SCIP_CUTOFF; else if (*result != SCIP_CUTOFF) *result = SCIP_SEPARATED; } /** add cut to global pool */ SCIP_CALL(SCIPaddPoolCut(scip, row)); /** release the row */ SCIP_CALL(SCIPreleaseRow(scip, &row)); } else { if (isOptimalityCut) { efficacy = rc->violated(vals) / cutrow.twoNorm(); isEfficacious = efficacy > 0.05; } else { efficacy = rc->violated(vals); isEfficacious = efficacy > 1.e-6; } DSPdebugMessage("%s efficacy %e\n", isOptimalityCut ? "Opti" : "Feas", efficacy); if (isEfficacious == TRUE) *result = SCIP_INFEASIBLE; } #endif } /** free memory */ SCIPfreeMemoryArray(scip, &vals); return SCIP_OKAY; }
/** adds given cut with two variables, if it is violated */ static SCIP_RETCODE addCut( SCIP* scip, /**< SCIP data structure */ SCIP_SEPA* sepa, /**< separator */ SCIP_SOL* sol, /**< the solution that should be separated, or NULL for LP solution */ SCIP_Real val1, /**< given coefficient of first variable */ SCIP_VAR* var1, /**< given first variable */ SCIP_Real solval1, /**< current LP solution value of first variable */ SCIP_Real val2, /**< given coefficient of second variable */ SCIP_VAR* var2, /**< given second variable */ SCIP_Real solval2, /**< current LP solution value of second variable */ SCIP_Real rhs, /**< given right hand side of the cut to add */ SCIP_Bool* cutoff, /**< whether a cutoff has been detected */ int* ncuts /**< pointer to update number of cuts added */ ) { SCIP_Real activity; assert(ncuts != NULL); assert(cutoff != NULL); *cutoff = FALSE; /* calculate activity of cut */ activity = val1 * solval1 + val2 * solval2; /*debugMessage(" -> %g<%s>[%g] + %g<%s>[%g] <= %g (act: %g)\n", val1, SCIPvarGetName(var1), solval1, val2, SCIPvarGetName(var2), solval2, rhs, activity);*/ /* check, if cut is violated */ if( SCIPisEfficacious(scip, activity - rhs) ) { SCIP_ROW* cut; char cutname[SCIP_MAXSTRLEN]; /* create cut */ (void) SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "implbd%d_%d", SCIPgetNLPs(scip), *ncuts); SCIP_CALL( SCIPcreateEmptyRowSepa(scip, &cut, sepa, cutname, -SCIPinfinity(scip), rhs, FALSE, FALSE, TRUE) ); SCIP_CALL( SCIPcacheRowExtensions(scip, cut) ); SCIP_CALL( SCIPaddVarToRow(scip, cut, var1, val1) ); SCIP_CALL( SCIPaddVarToRow(scip, cut, var2, val2) ); SCIP_CALL( SCIPflushRowExtensions(scip, cut) ); /* set cut rank: for implied bounds we always set to 1 */ SCIProwChgRank(cut, 1); #ifdef SCIP_DEBUG SCIPdebugMessage(" -> found cut (activity = %g): ", activity); SCIP_CALL( SCIPprintRow(scip, cut, NULL) ); #endif /* add cut */ SCIP_CALL( SCIPaddCut(scip, sol, cut, FALSE, cutoff) ); if ( ! (*cutoff) ) { SCIP_CALL( SCIPaddPoolCut(scip, cut) ); (*ncuts)++; } /* release cut */ SCIP_CALL( SCIPreleaseRow(scip, &cut) ); } return SCIP_OKAY; }