From e2bca528520a3f83070e233599e8d5dcd67c5efb Mon Sep 17 00:00:00 2001 From: Ted Ralphs Date: Tue, 29 Oct 2024 23:28:57 +0100 Subject: [PATCH] Adding parameters to allow more fine-grained control of cut generation --- src/MibSBilevel.cpp | 18 +++++-- src/MibSConstants.hpp | 22 +++++++++ src/MibSCutGenerator.cpp | 67 +++++++++++++++++++------ src/MibSModel.cpp | 102 ++++++++++++++++++++++++++++++--------- src/MibSParams.cpp | 31 +++++++----- src/MibSParams.hpp | 8 +-- 6 files changed, 191 insertions(+), 57 deletions(-) diff --git a/src/MibSBilevel.cpp b/src/MibSBilevel.cpp index bcd08cb7..d3ff9585 100644 --- a/src/MibSBilevel.cpp +++ b/src/MibSBilevel.cpp @@ -57,15 +57,19 @@ MibSBilevel::createBilevel(CoinPackedVector* sol, (model_->MibSPar_->entry(MibSParams::branchStrategy)); bool solveSecondLevelEveryIteration(model_->MibSPar_->entry - (MibSParams::solveSecondLevelEveryIteration) == PARAM_ON); + (MibSParams::solveSecondLevelEveryIteration) == PARAM_ON); + bool solveSecondLevelEveryIterationRoot(model_->MibSPar_->entry + (MibSParams::solveSecondLevelEveryIterationRoot) == PARAM_ON); bool solveSecondLevelWhenXYVarsInt(model_->MibSPar_->entry - (MibSParams::solveSecondLevelWhenXYVarsInt) == PARAM_ON); + (MibSParams::solveSecondLevelWhenXYVarsInt) == PARAM_ON); bool solveSecondLevelWhenXVarsInt(model_->MibSPar_->entry - (MibSParams::solveSecondLevelWhenXVarsInt) == PARAM_ON); + (MibSParams::solveSecondLevelWhenXVarsInt) == PARAM_ON); + bool solveSecondLevelWhenYVarsInt(model_->MibSPar_->entry + (MibSParams::solveSecondLevelWhenXVarsInt) == PARAM_ON); bool solveSecondLevelWhenLVarsInt(model_->MibSPar_->entry - (MibSParams::solveSecondLevelWhenLVarsInt) == PARAM_ON); + (MibSParams::solveSecondLevelWhenLVarsInt) == PARAM_ON); bool solveSecondLevelWhenLVarsFixed(model_->MibSPar_->entry - (MibSParams::solveSecondLevelWhenLVarsFixed) == PARAM_ON); + (MibSParams::solveSecondLevelWhenLVarsFixed) == PARAM_ON); int cutStrategy(model_->MibSPar_->entry (MibSParams::cutStrategy)); @@ -239,8 +243,11 @@ MibSBilevel::createBilevel(CoinPackedVector* sol, isLinkVarsFixed_) || (branchPar == MibSBranchingStrategyFractional && isIntegral_) || (solveSecondLevelEveryIteration) || + (solveSecondLevelEveryIterationRoot && + model_->activeNode_->getDepth() == 0) || (solveSecondLevelWhenXYVarsInt && isIntegral_) || (solveSecondLevelWhenXVarsInt && isUpperIntegral_) || + (solveSecondLevelWhenYVarsInt && isLowerIntegral_) || (solveSecondLevelWhenLVarsInt && isLinkVarsIntegral_) || (solveSecondLevelWhenLVarsFixed && isLinkVarsFixed_ )))){ storeSol = checkBilevelFeasibility(mibs->isRoot_); @@ -420,6 +427,7 @@ MibSBilevel::checkBilevelFeasibility(bool isRoot) }else{ startTimeVF = model_->broker_->subTreeTimer().getTime(); lSolver->branchAndBound(); + lSolver->writeLp("water"); model_->timerVF_ += model_->broker_->subTreeTimer().getTime() - startTimeVF; } diff --git a/src/MibSConstants.hpp b/src/MibSConstants.hpp index 05dd7104..fce58d31 100644 --- a/src/MibSConstants.hpp +++ b/src/MibSConstants.hpp @@ -98,6 +98,28 @@ enum MibSBilevelFreeSetTypeISIC{ //############################################################################# +enum MibSIDICGenStrategy{ + MibSIDICGenStrategyNotSet = -1, + MibSIDICGenStrategyAlways, + MibSIDICGenStrategyAlwaysRoot, + MibSIDICGenStrategyXYInt, + MibSIDICGenStrategyLInt, + MibSIDICGenStrategyYInt +}; + +//############################################################################# + +enum MibSISICGenStrategy{ + MibSISICGenStrategyNotSet = -1, + MibSISICGenStrategyAlways, + MibSISICGenStrategyAlwaysRoot, + MibSISICGenStrategyXYInt, + MibSISICGenStrategyLInt, + MibSISICGenStrategyYInt, +}; + +//############################################################################# + enum MibSRelaxTypeParamBoundCut{ MibSRelaxTypeParamBoundCutLP = 0, MibSRelaxTypeParamBoundCutMIP diff --git a/src/MibSCutGenerator.cpp b/src/MibSCutGenerator.cpp index ea80ddb8..d2327c42 100644 --- a/src/MibSCutGenerator.cpp +++ b/src/MibSCutGenerator.cpp @@ -5870,11 +5870,11 @@ MibSCutGenerator::generateConstraints(BcpsConstraintPool &conPool) int useBendersBinaryCut = localModel_->MibSPar_->entry(MibSParams::useBendersBinaryCut); - int useFractionalCuts = - localModel_->MibSPar_->entry(MibSParams::useFractionalCuts); + int IDICGenStrategy = + localModel_->MibSPar_->entry(MibSParams::IDICGenStrategy); - int useFractionalCutsRootOnly = - localModel_->MibSPar_->entry(MibSParams::useFractionalCutsRootOnly); + int ISICGenStrategy = + localModel_->MibSPar_->entry(MibSParams::ISICGenStrategy); double relaxedObjVal = localModel_->bS_->getLowerObj( localModel_->solver()->getColSolution(), @@ -6012,14 +6012,25 @@ MibSCutGenerator::generateConstraints(BcpsConstraintPool &conPool) numCuts += bendersInterdictionMultipleCuts(conPool); } } - if (useImprovingSolutionIC == PARAM_ON && ((haveSecondLevelSol && - relaxedObjVal > localModel_->bS_->objVal_ + localModel_->etol_) || - localModel_->MibSPar_->entry(MibSParams::bilevelFreeSetTypeISIC) == 1)){ + if (useImprovingSolutionIC == PARAM_ON && + ((haveSecondLevelSol && + relaxedObjVal > localModel_->bS_->objVal_ + localModel_->etol_) || + (localModel_->MibSPar_->entry(MibSParams::bilevelFreeSetTypeISIC) == + MibSBilevelFreeSetTypeISICWithNewLLSol && + ISICGenStrategy == MibSIDICGenStrategyLInt))){ cutType = MibSIntersectionCutImprovingSolution; numCuts += intersectionCuts(conPool, bS->optLowerSolutionOrd_, cutType); } - if (useFractionalCuts && useImprovingDirectionIC == PARAM_ON){ + if (useImprovingDirectionIC == PARAM_ON && + ((haveSecondLevelSol && + relaxedObjVal > localModel_->bS_->objVal_ + localModel_->etol_) || + (localModel_->MibSPar_->entry(MibSParams::bilevelFreeSetTypeISIC) == + MibSBilevelFreeSetTypeISICWithNewLLSol && + (ISICGenStrategy == MibSIDICGenStrategyLInt || + ISICGenStrategy == MibSIDICGenStrategyAlways || + (ISICGenStrategy == MibSIDICGenStrategyAlwaysRoot && + localModel_->activeNode_->getDepth() == 0))))){ cutType = MibSIntersectionCutImprovingDirection; numCuts += intersectionCuts(conPool, bS->optLowerSolutionOrd_, cutType); } @@ -6044,17 +6055,43 @@ MibSCutGenerator::generateConstraints(BcpsConstraintPool &conPool) //and should always be false (see BlisTreeNode.cpp) return (false); - }else if (bS->isLowerIntegral_ && - (useFractionalCuts || - (useFractionalCutsRootOnly && - localModel_->activeNode_->getDepth() == 0))){ - if (useImprovingDirectionIC == PARAM_ON){ + }else if (bS->isLowerIntegral_){ + if (useImprovingDirectionIC == PARAM_ON && + (IDICGenStrategy == MibSIDICGenStrategyAlways || + IDICGenStrategy == MibSIDICGenStrategyYInt || + (IDICGenStrategy == MibSIDICGenStrategyAlwaysRoot && + localModel_->activeNode_->getDepth() == 0))){ + cutType = MibSIntersectionCutImprovingDirection; + numCuts += intersectionCuts(conPool, bS->optLowerSolutionOrd_, cutType); + } + if (useImprovingSolutionIC == PARAM_ON && + ((haveSecondLevelSol && + relaxedObjVal > localModel_->bS_->objVal_ + localModel_->etol_) || + (localModel_->MibSPar_->entry(MibSParams::bilevelFreeSetTypeISIC) == + MibSBilevelFreeSetTypeISICWithNewLLSol && + (ISICGenStrategy == MibSIDICGenStrategyYInt || + ISICGenStrategy == MibSIDICGenStrategyAlways || + (ISICGenStrategy == MibSIDICGenStrategyAlwaysRoot && + localModel_->activeNode_->getDepth() == 0))))){ + cutType = MibSIntersectionCutImprovingSolution; + numCuts += intersectionCuts(conPool, bS->optLowerSolutionOrd_, cutType); + } + }else{ + if (useImprovingDirectionIC == PARAM_ON && + (IDICGenStrategy == MibSIDICGenStrategyAlways || + (IDICGenStrategy == MibSIDICGenStrategyAlwaysRoot && + localModel_->activeNode_->getDepth() == 0))){ cutType = MibSIntersectionCutImprovingDirection; numCuts += intersectionCuts(conPool, bS->optLowerSolutionOrd_, cutType); } - if (useImprovingSolutionIC == PARAM_ON && ((haveSecondLevelSol && + if (useImprovingSolutionIC == PARAM_ON && + ((haveSecondLevelSol && relaxedObjVal > localModel_->bS_->objVal_ + localModel_->etol_) || - localModel_->MibSPar_->entry(MibSParams::bilevelFreeSetTypeISIC) == 1)){ + (localModel_->MibSPar_->entry(MibSParams::bilevelFreeSetTypeISIC) == + MibSBilevelFreeSetTypeISICWithNewLLSol && + (ISICGenStrategy == MibSIDICGenStrategyAlways || + (ISICGenStrategy == MibSIDICGenStrategyAlwaysRoot && + localModel_->activeNode_->getDepth() == 0))))){ cutType = MibSIntersectionCutImprovingSolution; numCuts += intersectionCuts(conPool, bS->optLowerSolutionOrd_, cutType); } diff --git a/src/MibSModel.cpp b/src/MibSModel.cpp index cda009e5..37cf3233 100644 --- a/src/MibSModel.cpp +++ b/src/MibSModel.cpp @@ -3933,7 +3933,10 @@ MibSModel::adjustParameters() } } if (MibSPar_->entry(MibSParams::useImprovingDirectionIC) == PARAM_ON){ - defaultCutIsOn = true; + defaultCutIsOn = true; + if (MibSPar_->entry(MibSParams::IDICGenStrategy) == MibSIDICGenStrategyNotSet){ + MibSPar()->setEntry(MibSParams::IDICGenStrategy, MibSIDICGenStrategyXYInt); + } } //Param: "MibS_useImprovingSolutionIC" @@ -3958,8 +3961,8 @@ MibSModel::adjustParameters() std::cout << std::endl; MibSPar()->setEntry(MibSParams::useImprovingSolutionIC, PARAM_OFF); } - if (MibSPar_->entry(MibSParams::bilevelFreeSetTypeISIC) == 1 && - isLowerObjInt_ == false){ + if (MibSPar_->entry(MibSParams::bilevelFreeSetTypeISIC) == + MibSBilevelFreeSetTypeISICWithNewLLSol && isLowerObjInt_ == false){ std::cout << "The improving solution intersection cut (type II) are " << "only valid for problems with integer lower-level " << "objective coefficients."; @@ -3967,6 +3970,27 @@ MibSModel::adjustParameters() MibSPar()->setEntry(MibSParams::useImprovingSolutionIC, PARAM_OFF); } } + if (MibSPar_->entry(MibSParams::useImprovingSolutionIC) == PARAM_ON){ + switch (MibSPar_->entry(MibSParams::ISICGenStrategy)) { + case MibSISICGenStrategyNotSet: + MibSPar()->setEntry(MibSParams::ISICGenStrategy, MibSISICGenStrategyXYInt); + case MibSISICGenStrategyXYInt: + MibSPar()->setEntry(MibSParams::solveSecondLevelWhenXYVarsInt, 1); + break; + case MibSISICGenStrategyLInt: + MibSPar()->setEntry(MibSParams::solveSecondLevelWhenLVarsInt, 1); + break; + case MibSISICGenStrategyYInt: + MibSPar()->setEntry(MibSParams::solveSecondLevelWhenYVarsInt, 1); + break; + case MibSISICGenStrategyAlways: + MibSPar()->setEntry(MibSParams::solveSecondLevelEveryIteration, 1); + break; + case MibSISICGenStrategyAlwaysRoot: + MibSPar()->setEntry(MibSParams::solveSecondLevelEveryIterationRoot, 1); + break; + } + } //Param: "MibS_useHyperCubeIC" if ((turnOffDefaultCuts == true) && @@ -4155,36 +4179,68 @@ MibSModel::printProblemInfo(){ if (MibSPar_->entry(MibSParams::useImprovingSolutionIC) == PARAM_ON){ if (MibSPar_->entry(MibSParams::bilevelFreeSetTypeISIC) == MibSBilevelFreeSetTypeISICWithLLOptSol){ - std::cout << "Improving solution intersection cut generator (Type I) is on." << std::endl; + std::cout << "Improving solution intersection cut generator " + << "(Type I) is on." << std::endl; }else{ - std::cout << "Improving solution intersection cut generator (Type II) is on." << std::endl; + std::cout << "Improving solution intersection cut generator " + << "(Type II) is on." << std::endl; } + switch (MibSPar_->entry(MibSParams::ISICGenStrategy)){ + case MibSISICGenStrategyLInt: + std::cout << "ISICs will be used to separate solutions " + << "with integer linking variables." << std::endl; + break; + case MibSISICGenStrategyYInt: + std::cout << "ISICs will be used to separate solutions " + << "with integer lower-level variables." << std::endl; + break; + case MibSISICGenStrategyXYInt: + std::cout << "ISICs will be used to separate only solutions " + << "that are fully integer." << std::endl; + break; + case MibSISICGenStrategyAlwaysRoot: + std::cout << "ISICs will be used to separate fractional solutions " + << "only in the root node." << std::endl; + break; + case MibSISICGenStrategyAlways: + std::cout << "ISICs will be used to separate all solutions." + << std::endl; + break; + } } if (MibSPar_->entry(MibSParams::useImprovingDirectionIC) == PARAM_ON){ - std::cout << "Improving direction intersection cut generator is on." << std::endl; + std::cout << "Improving direction intersection cut generator is on." + << std::endl; + switch (MibSPar_->entry(MibSParams::IDICGenStrategy)){ + case MibSIDICGenStrategyLInt: + std::cout << "IDICs will be used to separate solutions " + << "with integer linking variables." << std::endl; + break; + case MibSIDICGenStrategyYInt: + std::cout << "IDICs will be used to separate solutions " + << "with integer lower-level variables." << std::endl; + break; + case MibSIDICGenStrategyXYInt: + std::cout << "IDICs will be used to separate only solutions " + << "that are fully integer." << std::endl; + break; + case MibSIDICGenStrategyAlwaysRoot: + std::cout << "IDICs will be used to separate fractional solutions " + << "only in the root node." << std::endl; + break; + case MibSIDICGenStrategyAlways: + std::cout << "IDICs will be used to separate all solutions." + << std::endl; + break; + } } if (MibSPar_->entry(MibSParams::useHypercubeIC) == PARAM_ON){ - std::cout << "Hypercube intersection cut generator is on." << std::endl; + std::cout << "Hypercube intersection cut generator is on." + << std::endl; } - if (MibSPar_->entry(MibSParams::useImprovingSolutionIC) == PARAM_ON || - MibSPar_->entry(MibSParams::useImprovingDirectionIC) == PARAM_ON){ - - if (MibSPar_->entry(MibSParams::useFractionalCutsRootOnly) == 1){ - std::cout << "Fractional solutions will be seperated in the root node." << std::endl; - MibSPar_->setEntry(MibSParams::useFractionalCuts, 0); - } - if (MibSPar_->entry(MibSParams::useFractionalCuts) == 1){ - std::cout << "Fractional solutions will be separated using intersection cuts." - << std::endl; - }else{ - std::cout << "Only integer solutions will be separated using intersection cuts." - << std::endl; - } - } - if (MibSPar_->entry(MibSParams::solveSecondLevelEveryIteration) == PARAM_OFF && MibSPar_->entry(MibSParams::solveSecondLevelWhenXYVarsInt) == diff --git a/src/MibSParams.cpp b/src/MibSParams.cpp index a7710ff7..be07edf3 100644 --- a/src/MibSParams.cpp +++ b/src/MibSParams.cpp @@ -177,12 +177,21 @@ MibSParams::createKeywordList() { keys_.push_back(make_pair(std::string("MibS_useTypeIC"), AlpsParameter(AlpsIntPar, useImprovingSolutionIC))); + keys_.push_back(make_pair(std::string("MibS_ISICGenStrategy"), + AlpsParameter(AlpsIntPar, ISICGenStrategy))); + keys_.push_back(make_pair(std::string("MibS_useImprovingDirectionIC"), AlpsParameter(AlpsIntPar, useImprovingDirectionIC))); //legacy parameter name keys_.push_back(make_pair(std::string("MibS_useTypeWatermelon"), AlpsParameter(AlpsIntPar, useImprovingDirectionIC))); + keys_.push_back(make_pair(std::string("MibS_IDICGenStrategy"), + AlpsParameter(AlpsIntPar, IDICGenStrategy))); + + keys_.push_back(make_pair(std::string("MibS_useTypeWatermelon"), + AlpsParameter(AlpsIntPar, useImprovingDirectionIC))); + keys_.push_back(make_pair(std::string("MibS_useHypercubeIC"), AlpsParameter(AlpsIntPar, useHypercubeIC))); @@ -195,23 +204,23 @@ MibSParams::createKeywordList() { keys_.push_back(make_pair(std::string("MibS_bilevelFreeSetTypeISIC"), AlpsParameter(AlpsIntPar, bilevelFreeSetTypeISIC))); - keys_.push_back(make_pair(std::string("MibS_useFractionalCuts"), - AlpsParameter(AlpsIntPar, useFractionalCuts))); - - keys_.push_back(make_pair(std::string("MibS_useFractionalCutsRootOnly"), - AlpsParameter(AlpsIntPar, - useFractionalCutsRootOnly))); - //solve lower-level Parameters keys_.push_back(make_pair(std::string("MibS_solveSecondLevelEveryIteration"), AlpsParameter(AlpsIntPar, solveSecondLevelEveryIteration))); + keys_.push_back(make_pair(std::string("MibS_solveSecondLevelEveryIterationRoot"), + AlpsParameter(AlpsIntPar, + solveSecondLevelEveryIterationRoot))); + keys_.push_back(make_pair(std::string("MibS_solveSecondLevelWhenXYVarsInt"), AlpsParameter(AlpsIntPar, solveSecondLevelWhenXYVarsInt))); keys_.push_back(make_pair(std::string("MibS_solveSecondLevelWhenXVarsInt"), AlpsParameter(AlpsIntPar, solveSecondLevelWhenXVarsInt))); + keys_.push_back(make_pair(std::string("MibS_solveSecondLevelWhenYVarsInt"), + AlpsParameter(AlpsIntPar, solveSecondLevelWhenYVarsInt))); + keys_.push_back(make_pair(std::string("MibS_solveSecondLevelWhenLVarsInt"), AlpsParameter(AlpsIntPar, solveSecondLevelWhenLVarsInt))); @@ -384,18 +393,18 @@ MibSParams::setDefaultEntries() { setEntry(useHybridIC, PARAM_NOTSET); - setEntry(useFractionalCuts, 1); - - setEntry(useFractionalCutsRootOnly, 0); - setEntry(bilevelFreeSetTypeISIC, MibSBilevelFreeSetTypeISICWithLLOptSol); setEntry(solveSecondLevelEveryIteration, PARAM_OFF); + setEntry(solveSecondLevelEveryIterationRoot, PARAM_OFF); + setEntry(solveSecondLevelWhenXYVarsInt, PARAM_ON); setEntry(solveSecondLevelWhenXVarsInt, PARAM_OFF); + setEntry(solveSecondLevelWhenYVarsInt, PARAM_OFF); + setEntry(solveSecondLevelWhenLVarsInt, PARAM_OFF); setEntry(solveSecondLevelWhenLVarsFixed, PARAM_ON); diff --git a/src/MibSParams.hpp b/src/MibSParams.hpp index 9d168670..cf5a7d3c 100644 --- a/src/MibSParams.hpp +++ b/src/MibSParams.hpp @@ -71,16 +71,18 @@ class MibSParams : public AlpsParameterSet { useBendersInterdictionCut, bendersInterdictionCutType, useImprovingSolutionIC, + ISICGenStrategy, + bilevelFreeSetTypeISIC, useImprovingDirectionIC, + IDICGenStrategy, useHypercubeIC, useTenderIC, useHybridIC, - useFractionalCuts, - useFractionalCutsRootOnly, - bilevelFreeSetTypeISIC, solveSecondLevelEveryIteration, + solveSecondLevelEveryIterationRoot, solveSecondLevelWhenXYVarsInt, solveSecondLevelWhenXVarsInt, + solveSecondLevelWhenYVarsInt, solveSecondLevelWhenLVarsInt, solveSecondLevelWhenLVarsFixed, computeBestUBWhenXVarsInt,