/** propagate the given binary variable/column using the root reduced cost stored in the SCIP internal data structers * and check if the implictions can be useful. Deppending on that implictions are used or not used during the search to * strength the reduced costs. */ static SCIP_RETCODE propagateRootRedcostBinvar( SCIP* scip, /**< SCIP data structure */ SCIP_PROPDATA* propdata, /**< propagator data structure */ SCIP_VAR* var, /**< variable to use for propagation */ SCIP_COL* col, /**< LP column of the variable */ SCIP_Real cutoffbound, /**< the current cutoff bound */ int* nchgbds /**< pointer to count the number of bound changes */ ) { SCIP_Real rootredcost; SCIP_Real rootsol; SCIP_Real rootlpobjval; assert(SCIPgetDepth(scip) == 0); /* skip binary variable if it is locally fixed */ if( SCIPvarGetLbLocal(var) > 0.5 || SCIPvarGetUbLocal(var) < 0.5 ) return SCIP_OKAY; rootredcost = SCIPvarGetBestRootRedcost(var); rootsol = SCIPvarGetBestRootSol(var); rootlpobjval = SCIPvarGetBestRootLPObjval(var); if( SCIPisFeasZero(scip, rootredcost) ) return SCIP_OKAY; assert(rootlpobjval != SCIP_INVALID); /*lint !e777*/ if( rootsol > 0.5 ) { assert(!SCIPisFeasPositive(scip, rootredcost)); /* update maximum reduced cost of a single binary variable */ propdata->maxredcost = MAX(propdata->maxredcost, -rootredcost); if( rootlpobjval - rootredcost > cutoffbound ) { SCIPdebugMessage("globally fix binary variable <%s> to 1.0\n", SCIPvarGetName(var)); SCIP_CALL( SCIPchgVarLb(scip, var, 1.0) ); (*nchgbds)++; return SCIP_OKAY; } } else { assert(!SCIPisFeasNegative(scip, rootredcost)); /* update maximum reduced cost of a single binary variable */ propdata->maxredcost = MAX(propdata->maxredcost, rootredcost); if( rootlpobjval + rootredcost > cutoffbound ) { SCIPdebugMessage("globally fix binary variable <%s> to 0.0\n", SCIPvarGetName(var)); SCIP_CALL( SCIPchgVarUb(scip, var, 0.0) ); (*nchgbds)++; return SCIP_OKAY; } } /* evaluate if the implications are useful; the implications are seen to be useful if they provide an increase for * the root reduced costs */ if( !propdata->usefullimplics ) { SCIP_Real lbredcost; SCIP_Real ubredcost; lbredcost = SCIPgetVarImplRedcost(scip, var, FALSE); assert(!SCIPisFeasPositive(scip, lbredcost)); ubredcost = SCIPgetVarImplRedcost(scip, var, TRUE); assert(!SCIPisFeasNegative(scip, ubredcost)); switch( SCIPcolGetBasisStatus(col) ) { case SCIP_BASESTAT_LOWER: ubredcost -= SCIPgetVarRedcost(scip, var); assert(!SCIPisFeasNegative(scip, ubredcost)); break; case SCIP_BASESTAT_UPPER: lbredcost -= SCIPgetVarRedcost(scip, var); assert(!SCIPisFeasPositive(scip, lbredcost)); break; case SCIP_BASESTAT_BASIC: case SCIP_BASESTAT_ZERO: default: break; } propdata->usefullimplics = (lbredcost < 0.0) || (ubredcost > 0.0); } return SCIP_OKAY; }
/** calculate score and preferred rounding direction for the candidate variable; the best candidate maximizes the * score */ static SCIP_DECL_DIVESETGETSCORE(divesetGetScoreFracdiving) { SCIP_Real obj; SCIP_Real objnorm; SCIP_Real objgain; SCIP_Bool mayrounddown; SCIP_Bool mayroundup; /* score fractionality if candidate is an SOS1 variable */ if ( divetype == SCIP_DIVETYPE_SOS1VARIABLE ) { *score = candsfrac; /* 'round' in nonzero direction, i.e., fix the candidates neighbors in the conflict graph to zero */ *roundup = SCIPisFeasPositive(scip, candsol); return SCIP_OKAY; } mayrounddown = SCIPvarMayRoundDown(cand); mayroundup = SCIPvarMayRoundUp(cand); /* choose rounding direction: * - if variable may be rounded in either both or neither direction, round corresponding to the fractionality * - otherwise, round in the infeasible direction, because feasible direction is tried by rounding * the current fractional solution */ if( mayrounddown != mayroundup ) *roundup = mayrounddown; else *roundup = (candsfrac > 0.5); obj = SCIPvarGetObj(cand); objnorm = SCIPgetObjNorm(scip); /* divide by objective norm to normalize obj into [-1,1] */ if( SCIPisPositive(scip, objnorm) ) obj /= objnorm; /* calculate objective gain and fractionality for the selected rounding direction */ if( *roundup ) { candsfrac = 1.0 - candsfrac; objgain = obj * candsfrac; } else objgain = -obj * candsfrac; assert(objgain >= -1.0 && objgain <= 1.0); /* penalize too small fractions */ if( candsfrac < 0.01 ) candsfrac += 10.0; /* prefer decisions on binary variables */ if( !SCIPvarIsBinary(cand) ) candsfrac *= 1000.0; /* prefer variables which cannot be rounded by scoring their fractionality */ if( !(mayrounddown || mayroundup) ) *score = -candsfrac; else *score = -2.0 - objgain; return SCIP_OKAY; }
/** propagate the given binary variable/column using the reduced cost */ static SCIP_RETCODE propagateRedcostBinvar( SCIP* scip, /**< SCIP data structure */ SCIP_PROPDATA* propdata, /**< propagator data structure */ SCIP_VAR* var, /**< variable to use for propagation */ SCIP_COL* col, /**< LP column of the variable */ SCIP_Real requiredredcost, /**< required reduset cost to be able to fix a binary variable */ int* nchgbds, /**< pointer to count the number of bound changes */ SCIP_Bool* cutoff /**< pointer to store if an cutoff was detected */ ) { SCIP_Real lbredcost; SCIP_Real ubredcost; SCIP_Real redcost; /* skip binary variable if it is locally fixed */ if( SCIPvarGetLbLocal(var) > 0.5 || SCIPvarGetUbLocal(var) < 0.5 ) return SCIP_OKAY; /* first use the redcost cost to fix the binary variable */ switch( SCIPcolGetBasisStatus(col) ) { case SCIP_BASESTAT_LOWER: redcost = SCIPgetVarRedcost(scip, var); assert(!SCIPisFeasNegative(scip, redcost)); if( redcost > requiredredcost ) { SCIPdebugMessage("variable <%s>: fixed 0.0 (requiredredcost <%g>, redcost <%g>)\n", SCIPvarGetName(var), requiredredcost, redcost); SCIP_CALL( SCIPchgVarUb(scip, var, 0.0) ); (*nchgbds)++; return SCIP_OKAY; } break; case SCIP_BASESTAT_UPPER: redcost = SCIPgetVarRedcost(scip, var); assert(!SCIPisFeasPositive(scip, redcost)); if( -redcost > requiredredcost ) { SCIPdebugMessage("variable <%s>: fixed 1.0 (requiredredcost <%g>, redcost <%g>)\n", SCIPvarGetName(var), requiredredcost, redcost); SCIP_CALL( SCIPchgVarLb(scip, var, 1.0) ); (*nchgbds)++; return SCIP_OKAY; } break; case SCIP_BASESTAT_BASIC: return SCIP_OKAY; case SCIP_BASESTAT_ZERO: assert(SCIPisFeasZero(scip, SCIPgetColRedcost(scip, col))); return SCIP_OKAY; default: SCIPerrorMessage("invalid basis state\n"); return SCIP_INVALIDDATA; } /* second, if the implications should be used and if the implications are seen to be promising used the implied * reduced costs to fix the binary variable */ if( propdata->useimplics && propdata->usefullimplics ) { /* collect implied reduced costs if the variable would be fixed to its lower bound */ lbredcost = SCIPgetVarImplRedcost(scip, var, FALSE); assert(!SCIPisFeasPositive(scip, lbredcost) || SCIPisFeasEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) ); /* collect implied reduced costs if the variable would be fixed to its upper bound */ ubredcost = SCIPgetVarImplRedcost(scip, var, TRUE); assert(!SCIPisFeasNegative(scip, ubredcost) || SCIPisFeasEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) ); if( -lbredcost > requiredredcost && ubredcost > requiredredcost ) { SCIPdebugMessage("variable <%s>: cutoff (requiredredcost <%g>, lbredcost <%g>, ubredcost <%g>)\n", SCIPvarGetName(var), requiredredcost, lbredcost, ubredcost); (*cutoff) = TRUE; } else if( -lbredcost > requiredredcost ) { SCIPdebugMessage("variable <%s>: fixed 1.0 (requiredredcost <%g>, redcost <%g>, lbredcost <%g>)\n", SCIPvarGetName(var), requiredredcost, redcost, lbredcost); SCIP_CALL( SCIPchgVarLb(scip, var, 1.0) ); (*nchgbds)++; } else if( ubredcost > requiredredcost ) { SCIPdebugMessage("variable <%s>: fixed 0.0 (requiredredcost <%g>, redcost <%g>, ubredcost <%g>)\n", SCIPvarGetName(var), requiredredcost, redcost, ubredcost); SCIP_CALL( SCIPchgVarUb(scip, var, 0.0) ); (*nchgbds)++; } /* update maximum reduced cost of a single binary variable */ propdata->maxredcost = MAX3(propdata->maxredcost, -lbredcost, ubredcost); } return SCIP_OKAY; }
/** propagate the given none binary variable/column using the reduced cost */ static SCIP_RETCODE propagateRedcostVar( SCIP* scip, /**< SCIP data structure */ SCIP_VAR* var, /**< variable to use for propagation */ SCIP_COL* col, /**< LP column of the variable */ SCIP_Real lpobjval, /**< objective value of the current LP */ SCIP_Real cutoffbound, /**< the current cutoff bound */ int* nchgbds /**< pointer to count the number of bound changes */ ) { SCIP_Real redcost; switch( SCIPcolGetBasisStatus(col) ) { case SCIP_BASESTAT_LOWER: redcost = SCIPgetColRedcost(scip, col); assert(!SCIPisFeasNegative(scip, redcost) || SCIPisFeasEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) ); if( SCIPisFeasPositive(scip, redcost) ) { SCIP_Real oldlb; SCIP_Real oldub; oldlb = SCIPvarGetLbLocal(var); oldub = SCIPvarGetUbLocal(var); assert(SCIPisEQ(scip, oldlb, SCIPcolGetLb(col))); assert(SCIPisEQ(scip, oldub, SCIPcolGetUb(col))); if( SCIPisFeasLT(scip, oldlb, oldub) ) { SCIP_Real newub; SCIP_Bool strengthen; /* calculate reduced cost based bound */ newub = (cutoffbound - lpobjval) / redcost + oldlb; /* check, if new bound is good enough: * - integer variables: take all possible strengthenings * - continuous variables: strengthening must cut part of the variable's dynamic range, and * at least 20% of the current domain */ if( SCIPvarIsIntegral(var) ) { newub = SCIPadjustedVarUb(scip, var, newub); strengthen = (newub < oldub - 0.5); } else strengthen = (newub < SCIPcolGetMaxPrimsol(col) && newub <= 0.2 * oldlb + 0.8 * oldub); if( strengthen ) { /* strengthen upper bound */ SCIPdebugMessage("redcost strengthening upper bound: <%s> [%g,%g] -> [%g,%g] (ub=%g, lb=%g, redcost=%g)\n", SCIPvarGetName(var), oldlb, oldub, oldlb, newub, cutoffbound, lpobjval, redcost); SCIP_CALL( SCIPchgVarUb(scip, var, newub) ); (*nchgbds)++; } } } break; case SCIP_BASESTAT_BASIC: break; case SCIP_BASESTAT_UPPER: redcost = SCIPgetColRedcost(scip, col); assert(!SCIPisFeasPositive(scip, redcost) || SCIPisFeasEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) ); if( SCIPisFeasNegative(scip, redcost) ) { SCIP_Real oldlb; SCIP_Real oldub; oldlb = SCIPvarGetLbLocal(var); oldub = SCIPvarGetUbLocal(var); assert(SCIPisEQ(scip, oldlb, SCIPcolGetLb(col))); assert(SCIPisEQ(scip, oldub, SCIPcolGetUb(col))); if( SCIPisFeasLT(scip, oldlb, oldub) ) { SCIP_Real newlb; SCIP_Bool strengthen; /* calculate reduced cost based bound */ newlb = (cutoffbound - lpobjval) / redcost + oldub; /* check, if new bound is good enough: * - integer variables: take all possible strengthenings * - continuous variables: strengthening must cut part of the variable's dynamic range, and * at least 20% of the current domain */ if( SCIPvarIsIntegral(var) ) { newlb = SCIPadjustedVarLb(scip, var, newlb); strengthen = (newlb > oldlb + 0.5); } else strengthen = (newlb > SCIPcolGetMinPrimsol(col) && newlb >= 0.8 * oldlb + 0.2 * oldub); /* check, if new bound is good enough: at least 20% strengthening for continuous variables */ if( strengthen ) { /* strengthen lower bound */ SCIPdebugMessage("redcost strengthening lower bound: <%s> [%g,%g] -> [%g,%g] (ub=%g, lb=%g, redcost=%g)\n", SCIPvarGetName(var), oldlb, oldub, newlb, oldub, cutoffbound, lpobjval, redcost); SCIP_CALL( SCIPchgVarLb(scip, var, newlb) ); (*nchgbds)++; } } } break; case SCIP_BASESTAT_ZERO: assert(SCIPisFeasZero(scip, SCIPgetColRedcost(scip, col))); break; default: SCIPerrorMessage("invalid basis state\n"); return SCIP_INVALIDDATA; } 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; }
/** transforms given solution of the master problem into solution of the original problem * @todo think about types of epsilons used in this method */ SCIP_RETCODE GCGrelaxTransformMastersolToOrigsol( SCIP* scip, /**< SCIP data structure */ SCIP_SOL* mastersol, /**< solution of the master problem, or NULL for current LP solution */ SCIP_SOL** origsol /**< pointer to store the new created original problem's solution */ ) { SCIP* masterprob; int npricingprobs; int* blocknrs; SCIP_Real* blockvalue; SCIP_Real increaseval; SCIP_VAR** mastervars; SCIP_Real* mastervals; int nmastervars; SCIP_VAR** vars; int nvars; SCIP_Real feastol; int i; int j; assert(scip != NULL); assert(origsol != NULL); masterprob = GCGrelaxGetMasterprob(scip); npricingprobs = GCGrelaxGetNPricingprobs(scip); assert( !SCIPisInfinity(scip, SCIPgetSolOrigObj(masterprob, mastersol)) ); SCIP_CALL( SCIPcreateSol(scip, origsol, GCGrelaxGetProbingheur(scip)) ); SCIP_CALL( SCIPallocBufferArray(scip, &blockvalue, npricingprobs) ); SCIP_CALL( SCIPallocBufferArray(scip, &blocknrs, npricingprobs) ); /* get variables of the master problem and their solution values */ SCIP_CALL( SCIPgetVarsData(masterprob, &mastervars, &nmastervars, NULL, NULL, NULL, NULL) ); assert(mastervars != NULL); assert(nmastervars >= 0); SCIP_CALL( SCIPallocBufferArray(scip, &mastervals, nmastervars) ); SCIP_CALL( SCIPgetSolVals(masterprob, mastersol, nmastervars, mastervars, mastervals) ); /* initialize the block values for the pricing problems */ for( i = 0; i < npricingprobs; i++ ) { blockvalue[i] = 0.0; blocknrs[i] = 0; } /* loop over all given master variables */ for( i = 0; i < nmastervars; i++ ) { SCIP_VAR** origvars; int norigvars; SCIP_Real* origvals; SCIP_Bool isray; int blocknr; origvars = GCGmasterVarGetOrigvars(mastervars[i]); norigvars = GCGmasterVarGetNOrigvars(mastervars[i]); origvals = GCGmasterVarGetOrigvals(mastervars[i]); blocknr = GCGvarGetBlock(mastervars[i]); isray = GCGmasterVarIsRay(mastervars[i]); assert(GCGvarIsMaster(mastervars[i])); assert(!SCIPisFeasNegative(scip, mastervals[i])); /** @todo handle infinite master solution values */ assert(!SCIPisInfinity(scip, mastervals[i])); /* first of all, handle variables representing rays */ if( isray ) { assert(blocknr >= 0); /* we also want to take into account variables representing rays, that have a small value (between normal and feas eps), * so we do no feas comparison here */ if( SCIPisPositive(scip, mastervals[i]) ) { /* loop over all original variables contained in the current master variable */ for( j = 0; j < norigvars; j++ ) { if( SCIPisZero(scip, origvals[j]) ) break; assert(!SCIPisZero(scip, origvals[j])); /* the original variable is a linking variable: just transfer the solution value of the direct copy (this is done later) */ if( GCGvarIsLinking(origvars[j]) ) continue; SCIPdebugMessage("Increasing value of %s by %f because of %s\n", SCIPvarGetName(origvars[j]), origvals[j] * mastervals[i], SCIPvarGetName(mastervars[i])); /* increase the corresponding value */ SCIP_CALL( SCIPincSolVal(scip, *origsol, origvars[j], origvals[j] * mastervals[i]) ); } } mastervals[i] = 0.0; continue; } /* handle the variables with value >= 1 to get integral values in original solution */ while( SCIPisFeasGE(scip, mastervals[i], 1.0) ) { /* variable was directly transferred to the master problem (only in linking conss or linking variable) */ /** @todo this may be the wrong place for this case, handle it before the while loop * and remove the similar case in the next while loop */ if( blocknr == -1 ) { assert(norigvars == 1); assert(origvals[0] == 1.0); /* increase the corresponding value */ SCIPdebugMessage("Increasing value of %s by %f because of %s\n", SCIPvarGetName(origvars[0]), origvals[0] * mastervals[i], SCIPvarGetName(mastervars[i])); SCIP_CALL( SCIPincSolVal(scip, *origsol, origvars[0], origvals[0] * mastervals[i]) ); mastervals[i] = 0.0; } else { assert(blocknr >= 0); /* loop over all original variables contained in the current master variable */ for( j = 0; j < norigvars; j++ ) { SCIP_VAR* pricingvar; int norigpricingvars; SCIP_VAR** origpricingvars; if( SCIPisZero(scip, origvals[j]) ) break; assert(!SCIPisZero(scip, origvals[j])); /* the original variable is a linking variable: just transfer the solution value of the direct copy (this is done above) */ if( GCGvarIsLinking(origvars[j]) ) continue; pricingvar = GCGoriginalVarGetPricingVar(origvars[j]); assert(GCGvarIsPricing(pricingvar)); norigpricingvars = GCGpricingVarGetNOrigvars(pricingvar); origpricingvars = GCGpricingVarGetOrigvars(pricingvar); /* just in case a variable has a value higher than the number of blocks, it represents */ if( norigpricingvars <= blocknrs[blocknr] ) { SCIPdebugMessage("Increasing value of %s by %f because of %s\n", SCIPvarGetName(origpricingvars[norigpricingvars-1]), mastervals[i] * origvals[j], SCIPvarGetName(mastervars[i])); /* increase the corresponding value */ SCIP_CALL( SCIPincSolVal(scip, *origsol, origpricingvars[norigpricingvars-1], mastervals[i] * origvals[j]) ); mastervals[i] = 1.0; } /* this should be default */ else { SCIPdebugMessage("Increasing value of %s by %f because of %s\n", SCIPvarGetName(origpricingvars[blocknrs[blocknr]]), origvals[j], SCIPvarGetName(mastervars[i]) ); /* increase the corresponding value */ SCIP_CALL( SCIPincSolVal(scip, *origsol, origpricingvars[blocknrs[blocknr]], origvals[j]) ); } } mastervals[i] = mastervals[i] - 1.0; blocknrs[blocknr]++; } } } /* loop over all given master variables */ for( i = 0; i < nmastervars; i++ ) { SCIP_VAR** origvars; int norigvars; SCIP_Real* origvals; int blocknr; origvars = GCGmasterVarGetOrigvars(mastervars[i]); norigvars = GCGmasterVarGetNOrigvars(mastervars[i]); origvals = GCGmasterVarGetOrigvals(mastervars[i]); blocknr = GCGvarGetBlock(mastervars[i]); if( SCIPisFeasZero(scip, mastervals[i]) ) { continue; } assert(SCIPisFeasGE(scip, mastervals[i], 0.0) && SCIPisFeasLT(scip, mastervals[i], 1.0)); while( SCIPisFeasPositive(scip, mastervals[i]) ) { assert(GCGvarIsMaster(mastervars[i])); assert(!GCGmasterVarIsRay(mastervars[i])); if( blocknr == -1 ) { assert(norigvars == 1); assert(origvals[0] == 1.0); SCIPdebugMessage("Increasing value of %s by %f because of %s\n", SCIPvarGetName(origvars[0]), origvals[0] * mastervals[i], SCIPvarGetName(mastervars[i]) ); /* increase the corresponding value */ SCIP_CALL( SCIPincSolVal(scip, *origsol, origvars[0], origvals[0] * mastervals[i]) ); mastervals[i] = 0.0; } else { increaseval = MIN(mastervals[i], 1.0 - blockvalue[blocknr]); /* loop over all original variables contained in the current master variable */ for( j = 0; j < norigvars; j++ ) { SCIP_VAR* pricingvar; int norigpricingvars; SCIP_VAR** origpricingvars; if( SCIPisZero(scip, origvals[j]) ) continue; /* the original variable is a linking variable: just transfer the solution value of the direct copy (this is done above) */ if( GCGvarIsLinking(origvars[j]) ) continue; pricingvar = GCGoriginalVarGetPricingVar(origvars[j]); assert(GCGvarIsPricing(pricingvar)); norigpricingvars = GCGpricingVarGetNOrigvars(pricingvar); origpricingvars = GCGpricingVarGetOrigvars(pricingvar); if( norigpricingvars <= blocknrs[blocknr] ) { increaseval = mastervals[i]; SCIPdebugMessage("Increasing value of %s by %f because of %s\n", SCIPvarGetName(origpricingvars[norigpricingvars-1]), origvals[j] * increaseval, SCIPvarGetName(mastervars[i]) ); /* increase the corresponding value */ SCIP_CALL( SCIPincSolVal(scip, *origsol, origpricingvars[norigpricingvars-1], origvals[j] * increaseval) ); } else { /* increase the corresponding value */ SCIPdebugMessage("Increasing value of %s by %f because of %s\n", SCIPvarGetName(origpricingvars[blocknrs[blocknr]]), origvals[j] * increaseval, SCIPvarGetName(mastervars[i]) ); SCIP_CALL( SCIPincSolVal(scip, *origsol, origpricingvars[blocknrs[blocknr]], origvals[j] * increaseval) ); } } mastervals[i] = mastervals[i] - increaseval; if( SCIPisFeasZero(scip, mastervals[i]) ) { mastervals[i] = 0.0; } blockvalue[blocknr] += increaseval; /* if the value assigned to the block is equal to 1, this block is full and we take the next block */ if( SCIPisFeasGE(scip, blockvalue[blocknr], 1.0) ) { blockvalue[blocknr] = 0.0; blocknrs[blocknr]++; } } } } SCIPfreeBufferArray(scip, &mastervals); SCIPfreeBufferArray(scip, &blocknrs); SCIPfreeBufferArray(scip, &blockvalue); /* if the solution violates one of its bounds by more than feastol * and less than 10*feastol, round it and print a warning */ SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL) ); SCIP_CALL( SCIPgetRealParam(scip, "numerics/feastol", &feastol) ); for( i = 0; i < nvars; ++i ) { SCIP_Real solval; SCIP_Real lb; SCIP_Real ub; solval = SCIPgetSolVal(scip, *origsol, vars[i]); lb = SCIPvarGetLbLocal(vars[i]); ub = SCIPvarGetUbLocal(vars[i]); if( SCIPisFeasGT(scip, solval, ub) && EPSEQ(solval, ub, 10 * feastol) ) { SCIP_CALL( SCIPsetSolVal(scip, *origsol, vars[i], ub) ); SCIPwarningMessage(scip, "Variable %s rounded from %g to %g in relaxation solution\n", SCIPvarGetName(vars[i]), solval, ub); } else if( SCIPisFeasLT(scip, solval, lb) && EPSEQ(solval, lb, 10 * feastol) ) { SCIP_CALL( SCIPsetSolVal(scip, *origsol, vars[i], lb) ); SCIPwarningMessage(scip, "Variable %s rounded from %g to %g in relaxation solution\n", SCIPvarGetName(vars[i]), solval, lb); } } 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_SOL* sol, /**< working solution */ SCIP_VAR* var, /**< variable to get the score value for */ SCIP_Real* downscore, /**< pointer to store the score for branching downwards */ SCIP_Real* upscore /**< pointer to store the score for branching upwards */ ) { SCIP_COL* col; SCIP_ROW** rows; SCIP_Real* vals; int nrows; int r; int nactrows; SCIP_Real nlprows; SCIP_Real downcoefsum; SCIP_Real upcoefsum; SCIP_Real score; assert(downscore != NULL); assert(upscore != NULL); *downscore = 0.0; *upscore = 0.0; if( SCIPvarGetStatus(var) != SCIP_VARSTATUS_COLUMN ) return 0.0; col = SCIPvarGetCol(var); assert(col != NULL); rows = SCIPcolGetRows(col); vals = SCIPcolGetVals(col); nrows = SCIPcolGetNLPNonz(col); nactrows = 0; downcoefsum = 0.0; upcoefsum = 0.0; for( r = 0; r < nrows; ++r ) { SCIP_ROW* row; SCIP_Real activity; SCIP_Real lhs; SCIP_Real rhs; SCIP_Real dualsol; row = rows[r]; /* calculate number of active constraint sides, i.e., count equations as two */ lhs = SCIProwGetLhs(row); rhs = SCIProwGetRhs(row); /* @todo this is suboptimal because activity is calculated by looping over all nonzeros of this row, need to * store LP activities instead (which cannot be retrieved if no LP was solved at this node) */ activity = SCIPgetRowSolActivity(scip, row, sol); dualsol = SCIProwGetDualsol(row); if( SCIPisFeasEQ(scip, activity, lhs) ) { SCIP_Real coef; nactrows++; coef = vals[r] / SCIProwGetNorm(row); if( SCIPisFeasPositive(scip, dualsol) ) { if( coef > 0.0 ) downcoefsum += coef; else upcoefsum -= coef; } } else if( SCIPisFeasEQ(scip, activity, rhs) ) { SCIP_Real coef; nactrows++; coef = vals[r] / SCIProwGetNorm(row); if( SCIPisFeasNegative(scip, dualsol) ) { if( coef > 0.0 ) upcoefsum += coef; else downcoefsum -= coef; } } } /* use the number of LP rows for normalization */ nlprows = (SCIP_Real)SCIPgetNLPRows(scip); upcoefsum /= nlprows; downcoefsum /= nlprows; /* calculate the score using SCIP's branch score. Pass NULL as variable to not have the var branch factor influence * the result */ score = nactrows / nlprows + SCIPgetBranchScore(scip, NULL, downcoefsum, upcoefsum); assert(score <= 3.0); assert(score >= 0.0); *downscore = downcoefsum; *upscore = upcoefsum; return score; }
/** execution method of primal heuristic */ static SCIP_DECL_HEUREXEC(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 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; }
/** 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; }
/** execution method of primal heuristic */ static SCIP_DECL_HEUREXEC(heurExecOctane) { /*lint --e{715}*/ SCIP_HEURDATA* heurdata; SCIP_SOL* sol; SCIP_SOL** first_sols; /* stores the first ffirst sols in order to check for common violation of a row */ SCIP_VAR** vars; /* the variables of the problem */ SCIP_VAR** fracvars; /* variables, that are fractional in current LP solution */ SCIP_VAR** subspacevars; /* the variables on which the search is performed. Either coinciding with vars or with the * space of all fractional variables of the current LP solution */ SCIP_Real p; /* n/2 - <delta,x> ( for some facet delta ) */ SCIP_Real q; /* <delta,a> */ SCIP_Real* rayorigin; /* origin of the ray, vector x in paper */ SCIP_Real* raydirection; /* direction of the ray, vector a in paper */ SCIP_Real* negquotient; /* negated quotient of rayorigin and raydirection, vector v in paper */ SCIP_Real* lambda; /* stores the distance of the facets (s.b.) to the origin of the ray */ SCIP_Bool usefracspace; /* determines whether the search concentrates on fractional variables and fixes integer ones */ SCIP_Bool cons_viol; /* used for checking whether a linear constraint is violated by one of the possible solutions */ SCIP_Bool success; SCIP_Bool* sign; /* signature of the direction of the ray */ SCIP_Bool** facets; /* list of extended facets */ int nvars; /* number of variables */ int nbinvars; /* number of 0-1-variables */ int nfracvars; /* number of fractional variables in current LP solution */ int nsubspacevars; /* dimension of the subspace on which the search is performed */ int nfacets; /* number of facets hidden by the ray that where already found */ int i; /* counter */ int j; /* counter */ int f_max; /* {0,1}-points to be checked */ int f_first; /* {0,1}-points to be generated at first in order to check whether a restart is necessary */ int r; /* counter */ int firstrule; int* perm; /* stores the way in which the coordinates were permuted */ int* fracspace; /* maps the variables of the subspace to the original variables */ assert(heur != NULL); assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0); assert(scip != NULL); assert(result != NULL); assert(SCIPhasCurrentNodeLP(scip)); *result = SCIP_DELAYED; /* only call heuristic, if an optimal LP solution is at hand */ if( SCIPgetLPSolstat(scip) != SCIP_LPSOLSTAT_OPTIMAL ) return SCIP_OKAY; *result = SCIP_DIDNOTRUN; SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, &nbinvars, NULL, NULL, NULL) ); /* OCTANE is for use in 0-1 programs only */ if( nvars != nbinvars ) return SCIP_OKAY; /* get heuristic's data */ heurdata = SCIPheurGetData(heur); assert( heurdata != NULL ); /* don't call heuristic, if it was not successful enough in the past */ /*lint --e{647}*/ if( SCIPgetNNodes(scip) % (SCIPheurGetNCalls(heur) / (100 * SCIPheurGetNBestSolsFound(heur) + 10*heurdata->nsuccess + 1) + 1) != 0 ) return SCIP_OKAY; SCIP_CALL( SCIPgetLPBranchCands(scip, &fracvars, NULL, NULL, &nfracvars, NULL) ); /* don't use integral starting points */ if( nfracvars == 0 ) return SCIP_OKAY; /* get working pointers from heurdata */ sol = heurdata->sol; assert( sol != NULL ); f_max = heurdata->f_max; f_first = heurdata->f_first; usefracspace = heurdata->usefracspace; SCIP_CALL( SCIPallocBufferArray(scip, &fracspace, nvars) ); /* determine the space one which OCTANE should work either as the whole space or as the space of fractional variables */ if( usefracspace ) { nsubspacevars = nfracvars; SCIP_CALL( SCIPallocBufferArray(scip, &subspacevars, nsubspacevars) ); BMScopyMemoryArray(subspacevars, fracvars, nsubspacevars); for( i = nvars - 1; i >= 0; --i ) fracspace[i] = -1; for( i = nsubspacevars - 1; i >= 0; --i ) fracspace[SCIPvarGetProbindex(subspacevars[i])] = i; } else { int currentindex; nsubspacevars = nvars; SCIP_CALL( SCIPallocBufferArray(scip, &subspacevars, nsubspacevars) ); /* only copy the variables which are in the current LP */ currentindex = 0; for( i = 0; i < nvars; ++i ) { if( SCIPcolGetLPPos(SCIPvarGetCol(vars[i])) >= 0 ) { subspacevars[currentindex] = vars[i]; fracspace[i] = currentindex; ++currentindex; } else { fracspace[i] = -1; --nsubspacevars; } } } /* nothing to do for empty search space */ if( nsubspacevars == 0 ) return SCIP_OKAY; assert(0 < nsubspacevars && nsubspacevars <= nvars); for( i = 0; i < nsubspacevars; i++) assert(fracspace[SCIPvarGetProbindex(subspacevars[i])] == i); /* at most 2^(n-1) facets can be hit */ if( nsubspacevars < 30 ) { /*lint --e{701}*/ assert(f_max > 0); f_max = MIN(f_max, 1 << (nsubspacevars - 1) ); } f_first = MIN(f_first, f_max); /* memory allocation */ SCIP_CALL( SCIPallocBufferArray(scip, &rayorigin, nsubspacevars) ); SCIP_CALL( SCIPallocBufferArray(scip, &raydirection, nsubspacevars) ); SCIP_CALL( SCIPallocBufferArray(scip, &negquotient, nsubspacevars) ); SCIP_CALL( SCIPallocBufferArray(scip, &sign, nsubspacevars) ); SCIP_CALL( SCIPallocBufferArray(scip, &perm, nsubspacevars) ); SCIP_CALL( SCIPallocBufferArray(scip, &lambda, f_max + 1) ); SCIP_CALL( SCIPallocBufferArray(scip, &facets, f_max + 1) ); for( i = f_max; i >= 0; --i ) { /*lint --e{866}*/ SCIP_CALL( SCIPallocBufferArray(scip, &facets[i], nsubspacevars) ); } SCIP_CALL( SCIPallocBufferArray(scip, &first_sols, f_first) ); *result = SCIP_DIDNOTFIND; /* starting OCTANE */ SCIPdebugMessage("run Octane heuristic on %s variables, which are %d vars, generate at most %d facets, using rule number %d\n", usefracspace ? "fractional" : "all", nsubspacevars, f_max, (heurdata->lastrule+1)%5); /* generate starting point in original coordinates */ SCIP_CALL( generateStartingPoint(scip, rayorigin, subspacevars, nsubspacevars) ); for( i = nsubspacevars - 1; i >= 0; --i ) rayorigin[i] -= 0.5; firstrule = heurdata->lastrule; ++firstrule; for( r = firstrule; r <= firstrule + 10 && !SCIPisStopped(scip); r++ ) { SCIP_ROW** rows; int nrows; /* generate shooting ray in original coordinates by certain rules */ switch(r % 5) { case 1: if( heurdata->useavgnbray ) { SCIP_CALL( generateAverageNBRay(scip, raydirection, fracspace, subspacevars, nsubspacevars) ); } break; case 2: if( heurdata->useobjray ) { SCIP_CALL( generateObjectiveRay(scip, raydirection, subspacevars, nsubspacevars) ); } break; case 3: if( heurdata->usediffray ) { SCIP_CALL( generateDifferenceRay(scip, raydirection, subspacevars, nsubspacevars) ); } break; case 4: if( heurdata->useavgwgtray && SCIPisLPSolBasic(scip) ) { SCIP_CALL( generateAverageRay(scip, raydirection, subspacevars, nsubspacevars, TRUE) ); } break; case 0: if( heurdata->useavgray && SCIPisLPSolBasic(scip) ) { SCIP_CALL( generateAverageRay(scip, raydirection, subspacevars, nsubspacevars, FALSE) ); } break; default: SCIPerrorMessage("invalid ray rule identifier\n"); SCIPABORT(); } /* there must be a feasible direction for the shooting ray */ if( isZero(scip, raydirection, nsubspacevars) ) continue; /* transform coordinates such that raydirection >= 0 */ flipCoords(rayorigin, raydirection, sign, nsubspacevars); for( i = f_max - 1; i >= 0; --i) lambda[i] = SCIPinfinity(scip); /* calculate negquotient, initialize perm, facets[0], p, and q */ p = 0.5 * nsubspacevars; q = 0.0; for( i = nsubspacevars - 1; i >= 0; --i ) { /* calculate negquotient, the ratio of rayorigin and raydirection, paying special attention to the case raydirection[i] == 0 */ if( SCIPisFeasZero(scip, raydirection[i]) ) { if( rayorigin[i] < 0 ) negquotient[i] = SCIPinfinity(scip); else negquotient[i] = -SCIPinfinity(scip); } else negquotient[i] = - (rayorigin[i] / raydirection[i]); perm[i] = i; /* initialization of facets[0] to the all-one facet with p and q its characteristic values */ facets[0][i] = TRUE; p -= rayorigin[i]; q += raydirection[i]; } assert(SCIPisPositive(scip, q)); /* resort the coordinates in nonincreasing order of negquotient */ SCIPsortDownRealRealRealBoolPtr( negquotient, raydirection, rayorigin, sign, (void**) subspacevars, nsubspacevars); #ifndef NDEBUG for( i = 0; i < nsubspacevars; i++ ) assert( raydirection[i] >= 0 ); for( i = 1; i < nsubspacevars; i++ ) assert( negquotient[i - 1] >= negquotient[i] ); #endif /* finished initialization */ /* find the first facet of the octahedron hit by a ray shot from rayorigin into direction raydirection */ for( i = 0; i < nsubspacevars && negquotient[i] * q > p; ++i ) { facets[0][i] = FALSE; p += 2 * rayorigin[i]; q -= 2 * raydirection[i]; assert(SCIPisPositive(scip, p)); assert(SCIPisPositive(scip, q)); } /* avoid dividing by values close to 0.0 */ if( !SCIPisFeasPositive(scip, q) ) continue; /* assert necessary for flexelint */ assert(q > 0); lambda[0] = p / q; nfacets = 1; /* find the first facets hit by the ray */ for( i = 0; i < nfacets && i < f_first; ++i) generateNeighborFacets(scip, facets, lambda, rayorigin, raydirection, negquotient, nsubspacevars, f_max, i, &nfacets); /* construct the first ffirst possible solutions */ for( i = 0; i < nfacets && i < f_first; ++i ) { SCIP_CALL( SCIPcreateSol(scip, &first_sols[i], heur) ); SCIP_CALL( getSolFromFacet(scip, facets[i], first_sols[i], sign, subspacevars, nsubspacevars) ); assert( first_sols[i] != NULL ); } /* try, whether there is a row violated by all of the first ffirst solutions */ cons_viol = FALSE; SCIP_CALL( SCIPgetLPRowsData(scip, &rows, &nrows) ); for( i = nrows - 1; i >= 0; --i ) { if( !SCIProwIsLocal(rows[i]) ) { SCIP_COL** cols; SCIP_Real constant; SCIP_Real lhs; SCIP_Real rhs; SCIP_Real rowval; SCIP_Real* coeffs; int nnonzerovars; int k; /* get the row's data */ constant = SCIProwGetConstant(rows[i]); lhs = SCIProwGetLhs(rows[i]); rhs = SCIProwGetRhs(rows[i]); coeffs = SCIProwGetVals(rows[i]); nnonzerovars = SCIProwGetNNonz(rows[i]); cols = SCIProwGetCols(rows[i]); rowval = constant; for( j = nnonzerovars - 1; j >= 0; --j ) rowval += coeffs[j] * SCIPgetSolVal(scip, first_sols[0], SCIPcolGetVar(cols[j])); /* if the row's lhs is violated by the first sol, test, whether it is violated by the next ones, too */ if( lhs > rowval ) { cons_viol = TRUE; for( k = MIN(f_first, nfacets) - 1; k > 0; --k ) { rowval = constant; for( j = nnonzerovars - 1; j >= 0; --j ) rowval += coeffs[j] * SCIPgetSolVal(scip, first_sols[k], SCIPcolGetVar(cols[j])); if( lhs <= rowval ) { cons_viol = FALSE; break; } } } /* dito for the right hand side */ else if( rhs < rowval ) { cons_viol = TRUE; for( k = MIN(f_first, nfacets) - 1; k > 0; --k ) { rowval = constant; for( j = nnonzerovars - 1; j >= 0; --j ) rowval += coeffs[j] * SCIPgetSolVal(scip, first_sols[k], SCIPcolGetVar(cols[j])); if( rhs >= rowval ) { cons_viol = FALSE; break; } } } /* break as soon as one row is violated by all of the ffirst solutions */ if( cons_viol ) break; } } if( !cons_viol ) { /* if there was no row violated by all solutions, try whether one or more of them are feasible */ for( i = MIN(f_first, nfacets) - 1; i >= 0; --i ) { assert(first_sols[i] != NULL); SCIP_CALL( SCIPtrySol(scip, first_sols[i], FALSE, TRUE, FALSE, TRUE, &success) ); if( success ) *result = SCIP_FOUNDSOL; } /* search for further facets and construct and try solutions out of facets fixed as closest ones */ for( i = f_first; i < f_max; ++i) { if( i >= nfacets ) break; generateNeighborFacets(scip, facets, lambda, rayorigin, raydirection, negquotient, nsubspacevars, f_max, i, &nfacets); SCIP_CALL( getSolFromFacet(scip, facets[i], sol, sign, subspacevars, nsubspacevars) ); SCIP_CALL( SCIPtrySol(scip, sol, FALSE, TRUE, FALSE, TRUE, &success) ); if( success ) *result = SCIP_FOUNDSOL; } } /* finished OCTANE */ for( i = MIN(f_first, nfacets) - 1; i >= 0; --i ) { SCIP_CALL( SCIPfreeSol(scip, &first_sols[i]) ); } } heurdata->lastrule = r; if( *result == SCIP_FOUNDSOL ) ++(heurdata->nsuccess); /* free temporary memory */ SCIPfreeBufferArray(scip, &first_sols); for( i = f_max; i >= 0; --i ) SCIPfreeBufferArray(scip, &facets[i]); SCIPfreeBufferArray(scip, &facets); SCIPfreeBufferArray(scip, &lambda); SCIPfreeBufferArray(scip, &perm); SCIPfreeBufferArray(scip, &sign); SCIPfreeBufferArray(scip, &negquotient); SCIPfreeBufferArray(scip, &raydirection); SCIPfreeBufferArray(scip, &rayorigin); SCIPfreeBufferArray(scip, &subspacevars); SCIPfreeBufferArray(scip, &fracspace); return SCIP_OKAY; }
/** generates all facets, from which facet i could be obtained by a decreasing + to - flip * or a nonincreasing - to + flip and tests whether they are among the fmax nearest ones */ static void generateNeighborFacets( SCIP* scip, /**< SCIP data structure */ SCIP_Bool** facets, /**< facets got so far */ SCIP_Real* lambda, /**< distances of the facets */ SCIP_Real* rayorigin, /**< origin of the shooting ray */ SCIP_Real* raydirection, /**< direction of the shooting ray */ SCIP_Real* negquotient, /**< array by which coordinates are sorted */ int nsubspacevars, /**< dimension of fractional space */ int f_max, /**< maximal number of facets to create */ int i, /**< current facet */ int* nfacets /**< number of facets */ ) { SCIP_Real p; SCIP_Real q; SCIP_Real lam; int minplus; int j; assert(scip != NULL); assert(facets != NULL); assert(facets[i] != NULL); assert(lambda != NULL); assert(rayorigin != NULL); assert(raydirection != NULL); assert(negquotient != NULL); assert(nfacets != NULL); assert(0 <= i && i < f_max); /* determine the p and q values of the next facet to fix as a closest one */ p = 0.5 * nsubspacevars; q = 0.0; for( j = nsubspacevars - 1; j >= 0; --j ) { if( facets[i][j] ) { p -= rayorigin[j]; q += raydirection[j]; } else { p += rayorigin[j]; q -= raydirection[j]; } } /* get the first + entry of the facet */ minplus = -1; for( j = 0; j < nsubspacevars; ++j ) { if( facets[i][j] ) { minplus = j; break; } } /* facet (- - ... -) cannot be hit, because raydirection >= 0 */ assert(minplus >= 0); assert(q != 0.0); assert(SCIPisFeasEQ(scip, lambda[i], p/q)); assert(lambda[i] >= 0.0); /* reverse search for facets from which the actual facet can be got by a single, decreasing + to - flip */ /* a facet will be inserted into the queue, iff it is one of the fmax closest ones already found */ for( j = 0; j < nsubspacevars && !facets[i][j] && SCIPisFeasGT(scip, negquotient[j], lambda[i]); ++j ) { if( SCIPisFeasPositive(scip, q + 2*raydirection[j]) ) { lam = (p - 2*rayorigin[j]) / (q + 2*raydirection[j]); tryToInsert(scip, facets, lambda, i, j, f_max, nsubspacevars, lam, nfacets); } } /* reverse search for facets from which the actual facet can be got by a single, nonincreasing - to + flip */ /* a facet will be inserted into the queue, iff it is one of the fmax closest ones already found */ for( j = nsubspacevars - 1; j >= 0 && facets[i][j] && SCIPisFeasLE(scip, negquotient[j], lambda[i]); --j ) { if( SCIPisFeasPositive(scip, q - 2*raydirection[j]) ) { lam = (p + 2*rayorigin[j]) / (q - 2*raydirection[j]); if( negquotient[minplus] <= lam ) tryToInsert(scip, facets, lambda, i, j, f_max, nsubspacevars, lam, nfacets); } } #ifndef NDEBUG for( j = 1; j < f_max; j++) assert(SCIPisFeasGE(scip, lambda[j], lambda[j-1])); #endif }
/** generates the direction of the shooting ray as the average of the normalized non-basic vars and rows */ static SCIP_RETCODE generateAverageNBRay( SCIP* scip, /**< SCIP data structure */ SCIP_Real* raydirection, /**< shooting ray */ int* fracspace, /**< index set of fractional variables */ SCIP_VAR** subspacevars, /**< pointer to fractional space variables */ int nsubspacevars /**< dimension of fractional space */ ) { SCIP_ROW** rows; SCIP_COL** cols; int nrows; int ncols; int i; assert(scip != NULL); assert(raydirection != NULL); assert(fracspace != NULL); assert(subspacevars != NULL); SCIP_CALL( SCIPgetLPRowsData(scip, &rows, &nrows) ); SCIP_CALL( SCIPgetLPColsData(scip, &cols, &ncols) ); /* add up non-basic variables */ for( i = nsubspacevars - 1; i >= 0; --i ) { SCIP_Real solval; solval = SCIPvarGetLPSol(subspacevars[i]); if( SCIPisFeasEQ(scip, solval, SCIPvarGetLbLocal(subspacevars[i])) ) raydirection[i] = +1.0; else if( SCIPisFeasEQ(scip, solval, SCIPvarGetUbLocal(subspacevars[i])) ) raydirection[i] = -1.0; else raydirection[i] = 0.0; } /* add up non-basic rows */ for( i = nrows - 1; i >= 0; --i ) { SCIP_Real dualsol; SCIP_Real factor; SCIP_Real* coeffs; SCIP_Real rownorm; int j; int nnonz; dualsol = SCIProwGetDualsol(rows[i]); if( SCIPisFeasPositive(scip, dualsol) ) factor = 1.0; else if( SCIPisFeasNegative(scip, dualsol) ) factor = -1.0; else continue; /* get the row's data */ coeffs = SCIProwGetVals(rows[i]); cols = SCIProwGetCols(rows[i]); nnonz = SCIProwGetNNonz(rows[i]); rownorm = 0.0; for( j = nnonz - 1; j >= 0; --j ) { SCIP_VAR* var; var = SCIPcolGetVar(cols[j]); if( fracspace[SCIPvarGetProbindex(var)] >= 0 ) rownorm += coeffs[j] * coeffs[j]; } if( SCIPisFeasZero(scip,rownorm) ) continue; else { assert(rownorm > 0); rownorm = SQRT(rownorm); } for( j = nnonz - 1; j >= 0; --j ) { SCIP_VAR* var; int f; var = SCIPcolGetVar(cols[j]); f = fracspace[SCIPvarGetProbindex(var)]; if( f >= 0 ) { raydirection[f] += factor * coeffs[j] / rownorm; assert(SCIP_REAL_MIN <= raydirection[f] && raydirection[f] <= SCIP_REAL_MAX); } } } return SCIP_OKAY; }