Skip to content

Commit

Permalink
Merge branch '3531-handle-infeasible-benders-subproblems' into 'v9-mi…
Browse files Browse the repository at this point in the history
…nor'

Resolve "massive numerical problems within default benders decomposition resulting in infeasible instances getting assigned an infeasible solution as optimal"

See merge request integer/scip!3540
  • Loading branch information
stephenjmaher committed Oct 21, 2024
2 parents 8dee6cf + 2037f11 commit f44f2a0
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 11 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,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
-------------------------

Expand All @@ -31,6 +34,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
Expand Down
74 changes: 65 additions & 9 deletions src/scip/benders.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -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*/
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 )
{
Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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);
}
}

Expand Down
8 changes: 8 additions & 0 deletions src/scip/benders.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
50 changes: 48 additions & 2 deletions src/scip/cons_benders.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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) );

Expand Down
8 changes: 8 additions & 0 deletions src/scip/pub_benders.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions src/scip/struct_benders.h
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down

0 comments on commit f44f2a0

Please sign in to comment.