Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/v9-minor'
Browse files Browse the repository at this point in the history
  • Loading branch information
scip-ci committed Oct 21, 2024
2 parents 0f9896e + f44f2a0 commit 0d11548
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 @@ -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
-------------------------

Expand All @@ -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
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 0d11548

Please sign in to comment.