diff --git a/CHANGELOG b/CHANGELOG index 7d4bdacf65..02fa1a7169 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -117,6 +117,9 @@ Performance improvements Fixed bugs ---------- +- Benders' decomposition subproblems that are always infeasible are correctly handled and the complete problem is + declared as infeasible. + Examples and applications ------------------------- @@ -129,6 +132,8 @@ Interface changes - added SCIPtpiGetLibraryName() and SCIPtpiGetLibraryDesc() - SCIPdelCons() can now also be called in SCIP_STAGE_TRANSFORMED - added SCIPstrcasecmp() and SCIPstrncasecmp() for case-insensitive string comparison +- added SCIPbendersSubproblemsAreInfeasible() to return if at least one subproblem has been identified as being + infeasible prior to performing any variable fixing. ### Command line interface ### Interfaces to external software diff --git a/src/scip/benders.c b/src/scip/benders.c index 480b3c453e..7695d89427 100644 --- a/src/scip/benders.c +++ b/src/scip/benders.c @@ -1491,18 +1491,19 @@ SCIP_RETCODE initialiseSubproblem( SCIP_BENDERS* benders, /**< Benders' decomposition */ SCIP_SET* set, /**< global SCIP settings */ int probnumber, /**< the subproblem number */ + SCIP_Bool* infeasible, /**< pointer to store whether the lp is detected as infeasible */ SCIP_Bool* success /**< was the initialisation process successful */ ) { SCIP* subproblem; SCIP_STATUS solvestatus; - SCIP_Bool cutoff; assert(benders != NULL); assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders)); assert(success != NULL); (*success) = FALSE; + (*infeasible) = FALSE; subproblem = SCIPbendersSubproblem(benders, probnumber); assert(subproblem != NULL); @@ -1516,8 +1517,9 @@ SCIP_RETCODE initialiseSubproblem( { assert(SCIPgetStage(subproblem) == SCIP_STAGE_SOLVING); - SCIP_CALL( SCIPconstructLP(subproblem, &cutoff) ); - (*success) = TRUE; + SCIP_CALL( SCIPconstructLP(subproblem, infeasible) ); + + (*success) = !(*infeasible); } return SCIP_OKAY; @@ -1531,7 +1533,8 @@ static SCIP_RETCODE initialiseLPSubproblem( SCIP_BENDERS* benders, /**< Benders' decomposition */ SCIP_SET* set, /**< global SCIP settings */ - int probnumber /**< the subproblem number */ + int probnumber, /**< the subproblem number */ + SCIP_Bool* infeasible /**< pointer to store whether the lp is detected as infeasible */ ) { SCIP* subproblem; @@ -1541,6 +1544,7 @@ SCIP_RETCODE initialiseLPSubproblem( assert(benders != NULL); assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders)); + assert(infeasible != NULL); subproblem = SCIPbendersSubproblem(benders, probnumber); assert(subproblem != NULL); @@ -1559,7 +1563,7 @@ SCIP_RETCODE initialiseLPSubproblem( assert(eventhdlr != NULL); /* calling an initial solve to put the problem into probing mode */ - SCIP_CALL( initialiseSubproblem(benders, set, probnumber, &success) ); + SCIP_CALL( initialiseSubproblem(benders, set, probnumber, infeasible, &success) ); return SCIP_OKAY; /*lint !e438*/ } @@ -1915,7 +1919,15 @@ SCIP_RETCODE createSubproblems( if( benders->benderssolvesubconvex == NULL && benders->benderssolvesub == NULL && SCIPgetStage(subproblem) <= SCIP_STAGE_PROBLEM ) { - SCIP_CALL( initialiseLPSubproblem(benders, set, i) ); + SCIP_Bool infeasible; + SCIP_CALL( initialiseLPSubproblem(benders, set, i, &infeasible) ); + + /* if the initialisation process indicates that the LP is infeasible, then the complete problem is + * infeasible. The subprobsinfeasible flag is set so that SCIP can be informed at the correct point + * during the solving process. + */ + if( infeasible ) + SCIPbendersSetSubproblemsAreInfeasible(benders, set); } } else @@ -4403,9 +4415,18 @@ SCIP_RETCODE SCIPbendersSetupSubproblem( } else { + SCIP_Bool infeasible; SCIP_Bool success; - SCIP_CALL( initialiseSubproblem(benders, set, probnumber, &success) ); + SCIP_CALL( initialiseSubproblem(benders, set, probnumber, &infeasible, &success) ); + assert(success == !infeasible); + + /* if the problem is identified as infeasible, this means that the underlying LP is infeasible. Since no variable + * fixings have been applied at this stage, this means that the complete problem is infeasible. It is only + * possible to set this parameter if we are at the root node or in an initialisation stage. + */ + if( infeasible ) + SCIPbendersSetSubproblemsAreInfeasible(benders, set); if( !success ) { @@ -4523,6 +4544,7 @@ SCIP_RETCODE SCIPbendersSolveSubproblem( assert(set != NULL); assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders)); + assert(infeasible != NULL); (*infeasible) = FALSE; /* the subproblem must be set up before this function is called. */ @@ -4588,7 +4610,7 @@ SCIP_RETCODE SCIPbendersSolveSubproblem( } else { - SCIP_CALL( initialiseSubproblem(benders, set, probnumber, &success) ); + SCIP_CALL( initialiseSubproblem(benders, set, probnumber, infeasible, &success) ); } /* if setting up the subproblem was successful */ @@ -6455,6 +6477,33 @@ SCIP_Bool SCIPbendersInStrengthenRound( return benders->strengthenround; } +/** sets the flag to indicate that at least one subproblem is always infeasible + * NOTE: this is without any variable fixing being performed + */ +void SCIPbendersSetSubproblemsAreInfeasible( + SCIP_BENDERS* benders, /**< Benders' decomposition */ + SCIP_SET* set /**< global SCIP settings */ + ) +{ + assert(benders != NULL); + assert(set != NULL); + + if( SCIPgetDepth(set->scip) <= 0 ) + benders->subprobsinfeasible = TRUE; +} + +/** returns whether at least one of the subproblems has been identified as infeasible. + * NOTE: this is without any variable fixing being performed + */ +SCIP_Bool SCIPbendersSubproblemsAreInfeasible( + SCIP_BENDERS* benders /**< Benders' decomposition */ + ) +{ + assert(benders != NULL); + + return benders->subprobsinfeasible; +} + /** changes all of the master problem variables in the given subproblem to continuous. */ SCIP_RETCODE SCIPbendersChgMastervarsToCont( SCIP_BENDERS* benders, /**< Benders' decomposition */ @@ -6525,7 +6574,14 @@ SCIP_RETCODE SCIPbendersChgMastervarsToCont( */ if( SCIPbendersGetSubproblemType(benders, probnumber) == SCIP_BENDERSSUBTYPE_CONVEXCONT ) { - SCIP_CALL( initialiseLPSubproblem(benders, set, probnumber) ); + SCIP_CALL( initialiseLPSubproblem(benders, set, probnumber, &infeasible) ); + + /* if the initialisation process indicates that the LP is infeasible, then the complete problem is + * infeasible. The subprobsinfeasible flag is set so that SCIP can be informed at the correct point + * during the solving process. + */ + if( infeasible ) + SCIPbendersSetSubproblemsAreInfeasible(benders, set); } } diff --git a/src/scip/benders.h b/src/scip/benders.h index db9a50c568..ef85175447 100644 --- a/src/scip/benders.h +++ b/src/scip/benders.h @@ -393,6 +393,14 @@ void SCIPbendersSetSubproblemEnabled( SCIP_Bool enabled /**< flag to indicate whether the subproblem is enabled */ ); +/** sets the flag to indicate that at least one subproblem is always infeasible + * NOTE: this is without any variable fixing being performed + */ +void SCIPbendersSetSubproblemsAreInfeasible( + SCIP_BENDERS* benders, /**< Benders' decomposition */ + SCIP_SET* set /**< global SCIP settings */ + ); + /** changes all of the master problem variables in the given subproblem to continuous */ SCIP_RETCODE SCIPbendersChgMastervarsToCont( SCIP_BENDERS* benders, /**< Benders' decomposition */ diff --git a/src/scip/cons_benders.c b/src/scip/cons_benders.c index f4dc1e4106..801d496b2b 100644 --- a/src/scip/cons_benders.c +++ b/src/scip/cons_benders.c @@ -285,6 +285,19 @@ SCIP_RETCODE SCIPconsBendersEnforceSolution( for( i = 0; i < nactivebenders; i++ ) { + /* if any subproblems are declared as infeasible, then the result must be returned as infeasible. It is not + * possible to generate cuts, since the LP is infeasible without performing any master variable fixing + */ + if( SCIPbendersSubproblemsAreInfeasible(benders[i]) ) + { + (*result) = SCIP_INFEASIBLE; + + /* the Benders' decomposition subproblems do not need to be checked, since there is a subproblem that is + * infeasible. + */ + break; + } + switch( type ) { case SCIP_BENDERSENFOTYPE_LP: @@ -437,6 +450,27 @@ SCIP_DECL_CONSEXIT(consExitBenders) } +/** LP initialization method of constraint handler (called before the initial LP relaxation at a node is solved) */ +static +SCIP_DECL_CONSINITLP(consInitlpBenders) +{ /*lint --e{715}*/ + SCIP_BENDERS** benders; + int nactivebenders; + int i; + + assert(scip != NULL); + + benders = SCIPgetBenders(scip); + nactivebenders = SCIPgetNActiveBenders(scip); + + (*infeasible) = FALSE; + + /* checking all Benders' decomposition implementations to see if any subproblems have been declared as infeasible */ + for( i = 0; i < nactivebenders && !(*infeasible); i++ ) + (*infeasible) = SCIPbendersSubproblemsAreInfeasible(benders[i]); + + return SCIP_OKAY; +} /** constraint enforcing method of constraint handler for LP solutions */ static @@ -561,8 +595,19 @@ SCIP_DECL_CONSCHECK(consCheckBenders) { for( i = 0; i < nactivebenders; i++ ) { - SCIP_CALL( SCIPsolveBendersSubproblems(scip, benders[i], sol, result, &infeasible, &auxviol, - SCIP_BENDERSENFOTYPE_CHECK, TRUE) ); + /* if any subproblems are declared as infeasible, then the result must be returned as infeasible. It is not + * possible to generate cuts, since the LP is infeasible without performing any master variable fixing + */ + if( SCIPbendersSubproblemsAreInfeasible(benders[i]) ) + { + infeasible = TRUE; + (*result) = SCIP_INFEASIBLE; + } + else + { + SCIP_CALL( SCIPsolveBendersSubproblems(scip, benders[i], sol, result, &infeasible, &auxviol, + SCIP_BENDERSENFOTYPE_CHECK, TRUE) ); + } /* in the case of multiple Benders' decompositions, the subproblems are solved until a constriant is added or * infeasibility is proven. So if the result is not SCIP_FEASIBLE, then the loop is exited */ @@ -791,6 +836,7 @@ SCIP_RETCODE SCIPincludeConshdlrBenders( SCIP_CALL( SCIPsetConshdlrExit(scip, conshdlr, consExitBenders) ); SCIP_CALL( SCIPsetConshdlrCopy(scip, conshdlr, conshdlrCopyBenders, NULL) ); SCIP_CALL( SCIPsetConshdlrFree(scip, conshdlr, consFreeBenders) ); + SCIP_CALL( SCIPsetConshdlrInitlp(scip, conshdlr, consInitlpBenders) ); SCIP_CALL( SCIPsetConshdlrEnforelax(scip, conshdlr, consEnforelaxBenders) ); SCIP_CALL( SCIPsetConshdlrPresol(scip, conshdlr, consPresolBenders, CONSHDLR_MAXPREROUNDS, CONSHDLR_PRESOLTIMING) ); diff --git a/src/scip/pub_benders.h b/src/scip/pub_benders.h index 1458da337d..42029134fc 100644 --- a/src/scip/pub_benders.h +++ b/src/scip/pub_benders.h @@ -413,6 +413,14 @@ SCIP_Bool SCIPbendersInStrengthenRound( SCIP_BENDERS* benders /**< Benders' decomposition */ ); +/** returns whether at least one of the subproblems has been identified as infeasible. + * NOTE: this is without any variable fixing being performed + */ +SCIP_EXPORT +SCIP_Bool SCIPbendersSubproblemsAreInfeasible( + SCIP_BENDERS* benders /**< Benders' decomposition */ + ); + /** solves the LP of the Benders' decomposition subproblem * * This requires that the subproblem is in probing mode. diff --git a/src/scip/struct_benders.h b/src/scip/struct_benders.h index a0cbddbc36..bd223dfb11 100644 --- a/src/scip/struct_benders.h +++ b/src/scip/struct_benders.h @@ -131,6 +131,8 @@ struct SCIP_Benders int nnonlinearsubprobs; /**< the number of subproblems that are non-linear */ SCIP_Bool subprobscreated; /**< have the subproblems been created for this Benders' decomposition. This flag is used when retransforming the problem.*/ + SCIP_Bool subprobsinfeasible; /**< flag to indicate that infeasibility of at least one subproblem has + been detected in the initialisation stages. */ SCIP_Bool* mastervarscont; /**< flag to indicate that the master problem variable have been converted to continuous variables. */ SCIP_Bool* subprobsetup; /**< flag to indicate whether the subproblem has been set up. */