Scippy

    SCIP

    Solving Constraint Integer Programs

    cons_benderslp.c
    Go to the documentation of this file.
    1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    2/* */
    3/* This file is part of the program and library */
    4/* SCIP --- Solving Constraint Integer Programs */
    5/* */
    6/* Copyright (c) 2002-2025 Zuse Institute Berlin (ZIB) */
    7/* */
    8/* Licensed under the Apache License, Version 2.0 (the "License"); */
    9/* you may not use this file except in compliance with the License. */
    10/* You may obtain a copy of the License at */
    11/* */
    12/* http://www.apache.org/licenses/LICENSE-2.0 */
    13/* */
    14/* Unless required by applicable law or agreed to in writing, software */
    15/* distributed under the License is distributed on an "AS IS" BASIS, */
    16/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
    17/* See the License for the specific language governing permissions and */
    18/* limitations under the License. */
    19/* */
    20/* You should have received a copy of the Apache-2.0 license */
    21/* along with SCIP; see the file LICENSE. If not visit scipopt.org. */
    22/* */
    23/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    24
    25/**@file cons_benderslp.c
    26 * @ingroup DEFPLUGINS_CONS
    27 * @brief constraint handler for benderslp decomposition
    28 * @author Stephen J. Maher
    29 *
    30 * Two constraint handlers are implemented for the generation of Benders' decomposition cuts. When included in a
    31 * problem, the Benders' decomposition constraint handlers generate cuts during the enforcement of LP and relaxation
    32 * solutions. Additionally, Benders' decomposition cuts can be generated when checking the feasibility of solutions with
    33 * respect to the subproblem constraints.
    34 *
    35 * This constraint handler has an enforcement priority that is greater than the integer constraint handler. This means
    36 * that all LP solutions will be first checked for feasibility with respect to the Benders' decomposition second stage
    37 * constraints before performing an integrality check. This is part of a multi-phase approach for solving mixed integer
    38 * programs by Benders' decomposition.
    39 *
    40 * A parameter is available to control the depth at which the non-integer LP solution are enforced by solving the
    41 * Benders' decomposition subproblems. This parameter is set to 0 by default, indicating that non-integer LP solutions
    42 * are enforced only at the root node.
    43 */
    44
    45/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
    46
    47#include <assert.h>
    48
    49#include "scip/scip.h"
    50#include "scip/cons_benderslp.h"
    51#include "scip/cons_benders.h"
    52
    53
    54/* fundamental constraint handler properties */
    55#define CONSHDLR_NAME "benderslp"
    56#define CONSHDLR_DESC "constraint handler for Benders' Decomposition to separate LP solutions"
    57#define CONSHDLR_ENFOPRIORITY 10000000 /**< priority of the constraint handler for constraint enforcing */
    58#define CONSHDLR_CHECKPRIORITY 10000000 /**< priority of the constraint handler for checking feasibility */
    59#define CONSHDLR_EAGERFREQ 100 /**< frequency for using all instead of only the useful constraints in separation,
    60 * propagation and enforcement, -1 for no eager evaluations, 0 for first only */
    61#define CONSHDLR_NEEDSCONS FALSE /**< should the constraint handler be skipped, if no constraints are available? */
    62
    63
    64#define DEFAULT_CONSBENDERSLP_MAXDEPTH 0/**< depth at which Benders' decomposition cuts are generated from the LP
    65 * solution (-1: always, 0: only at root) */
    66#define DEFAULT_CONSBENDERSLP_FREQ 0/**< the depth frequency for generating LP cuts after the max depth is reached (0: never, 1: all nodes, ...) */
    67#define DEFAULT_CONSBENDERSLP_STALLLIMIT 100/**< the number of nodes processed without a dual bound improvement before enforcing the LP relaxation, 0: no stall count applied */
    68#define DEFAULT_CONSBENDERSLP_ITERLIMIT 100 /**< after the root node, only iterlimit fractional LP solutions are used at each node to generate Benders' decomposition cuts. */
    69#define DEFAULT_ACTIVE FALSE /**< is the constraint handler active? */
    70
    71/*
    72 * Data structures
    73 */
    74
    75/** constraint handler data */
    76struct SCIP_ConshdlrData
    77{
    78 /* parameters for controlling the two-phase method for Benders' decomposition */
    79 int maxdepth; /**< the maximum depth at which Benders' cuts are generated from the LP */
    80 int freq; /**< the depth frequency of generating LP cuts after the max depth is reached */
    81 SCIP_Bool active; /**< is the constraint handler active? */
    82
    83 /* variable used to control the behaviour of the two-phase method for Benders' decomposition */
    84 SCIP_Longint ncallsnode; /**< the number of calls at the current node */
    85 SCIP_NODE* currnode; /**< the current node */
    86 SCIP_Real prevbound; /**< the previous dual bound */
    87 int iterlimit; /**< the iteration limit for the first phase of the two-phase method at a node lower than the root. */
    88 int stallcount; /**< the number of nodes processed since the last lower bound increase */
    89 int stalllimit; /**< the number of nodes processed without bound improvement before enforcing the LP relaxation */
    90};
    91
    92
    93/*
    94 * Local methods
    95 */
    96
    97
    98/*
    99 * Callback methods of constraint handler
    100 */
    101
    102/** copy method for constraint handler plugins (called when SCIP copies plugins) */
    103static
    104SCIP_DECL_CONSHDLRCOPY(conshdlrCopyBenderslp)
    105{ /*lint --e{715}*/
    106 assert(scip != NULL);
    107
    109
    110 *valid = TRUE;
    111
    112 return SCIP_OKAY;
    113}
    114
    115
    116/** destructor of constraint handler to free constraint handler data (called when SCIP is exiting) */
    117static
    118SCIP_DECL_CONSFREE(consFreeBenderslp)
    119{ /*lint --e{715}*/
    120 SCIP_CONSHDLRDATA* conshdlrdata;
    121
    122 assert(scip != NULL);
    123 assert(conshdlr != NULL);
    124
    125 conshdlrdata = SCIPconshdlrGetData(conshdlr);
    126 assert(conshdlrdata != NULL);
    127
    128 /* freeing the constraint handler data */
    129 SCIPfreeMemory(scip, &conshdlrdata);
    130
    131 return SCIP_OKAY;
    132}
    133
    134
    135/** initialization method of constraint handler (called after problem was transformed) */
    136static
    137SCIP_DECL_CONSINIT(consInitBenderslp)
    138{ /*lint --e{715}*/
    139 SCIP_CONSHDLRDATA* conshdlrdata;
    140
    141 assert(conshdlr != NULL);
    142
    143 conshdlrdata = SCIPconshdlrGetData(conshdlr);
    144 assert(conshdlrdata != NULL);
    145
    146 /* disable benderslp handler */
    147 SCIPconshdlrSetNeedsCons(conshdlr, !conshdlrdata->active);
    148
    149 return SCIP_OKAY;
    150}
    151
    152
    153/** deinitialization method of constraint handler (called before transformed problem is freed) */
    154static
    155SCIP_DECL_CONSEXIT(consExitBenderslp)
    156{ /*lint --e{715}*/
    157 assert(conshdlr != NULL);
    158 assert(!SCIPconshdlrNeedsCons(conshdlr) || !SCIPconshdlrGetData(conshdlr)->active);
    159
    160 /* reenable benderslp handler */
    162
    163 return SCIP_OKAY;
    164}
    165
    166
    167/** constraint enforcing method of constraint handler for LP solutions */
    168static
    169SCIP_DECL_CONSENFOLP(consEnfolpBenderslp)
    170{ /*lint --e{715}*/
    171 SCIP_CONSHDLRDATA* conshdlrdata;
    172
    173 assert(conshdlr != NULL);
    174
    175 conshdlrdata = SCIPconshdlrGetData(conshdlr);
    176
    177 /* the result is initially set to FEASIBLE. If the two-phase method is not executed, then the result will remain as
    178 * FEASIBLE. The actual feasibility of the Benders' decomposition subproblems is checked in cons_benders.
    179 */
    180 (*result) = SCIP_FEASIBLE;
    181
    182 /* updating the stall count. If the bound has improved since the last call, then the stall count is set to zero */
    183 conshdlrdata->stallcount++;
    184 if( SCIPisLT(scip, conshdlrdata->prevbound, SCIPgetLowerbound(scip)) )
    185 conshdlrdata->stallcount = 0;
    186
    187 conshdlrdata->prevbound = SCIPgetLowerbound(scip);
    188 conshdlrdata->ncallsnode++;
    189
    190 /* if a new node is being processed, then the iteration counts are reset. */
    191 if( conshdlrdata->currnode != SCIPgetCurrentNode(scip) )
    192 {
    193 conshdlrdata->currnode = SCIPgetCurrentNode(scip);
    194 conshdlrdata->ncallsnode = 0;
    195 }
    196
    197 /* checking whether the two-phase method is performed.
    198 * - If a maxdepth is specified
    199 * - current depth is checked
    200 * - the frequency is checked, if a frequency is specified
    201 * - the stalllimit is checked if a stalllimit is specified.
    202 */
    203 if( conshdlrdata->maxdepth >= 0 && SCIPgetDepth(scip) > conshdlrdata->maxdepth
    204 && (conshdlrdata->freq == 0 || SCIPgetDepth(scip) % conshdlrdata->freq != 0)
    205 && (conshdlrdata->stalllimit == 0 || conshdlrdata->stallcount < conshdlrdata->stalllimit) )
    206 return SCIP_OKAY;
    207
    208 /* if an iteration limit is specified, then this is imposed at nodes after the root node */
    209 if( SCIPgetDepth(scip) > 0 && conshdlrdata->ncallsnode >= conshdlrdata->iterlimit )
    210 return SCIP_OKAY;
    211
    212 /* the two phase method is only performed at the root node for sub-SCIPs */
    213 if( SCIPgetSubscipDepth(scip) > 0 && SCIPgetDepth(scip) > 0 )
    214 return SCIP_OKAY;
    215
    216 /* checking the Benders' decomposition subproblems for feasibility. */
    218
    219 /* if the stalllimit is exceeded and the subproblem were checked, then the stall count is reset to zero */
    220 if( conshdlrdata->stallcount >= conshdlrdata->stalllimit )
    221 conshdlrdata->stallcount = 0;
    222
    223 return SCIP_OKAY;
    224}
    225
    226
    227/** constraint enforcing method of constraint handler for relaxation solutions */
    228static
    229SCIP_DECL_CONSENFORELAX(consEnforelaxBenderslp)
    230{ /*lint --e{715}*/
    231 SCIP_CONSHDLRDATA* conshdlrdata;
    232
    233 assert(conshdlr != NULL);
    234
    235 conshdlrdata = SCIPconshdlrGetData(conshdlr);
    236
    237 if( conshdlrdata->maxdepth >= 0 && SCIPgetDepth(scip) > conshdlrdata->maxdepth )
    238 (*result) = SCIP_FEASIBLE;
    239 else
    241
    242 return SCIP_OKAY;
    243}
    244
    245
    246/** constraint enforcing method of constraint handler for pseudo solutions */
    247static
    248SCIP_DECL_CONSENFOPS(consEnfopsBenderslp)
    249{ /*lint --e{715}*/
    250 SCIP_CONSHDLRDATA* conshdlrdata;
    251
    252 assert(conshdlr != NULL);
    253
    254 conshdlrdata = SCIPconshdlrGetData(conshdlr);
    255
    256 if( conshdlrdata->maxdepth >= 0 && SCIPgetDepth(scip) > conshdlrdata->maxdepth )
    257 (*result) = SCIP_FEASIBLE;
    258 else
    260
    261 return SCIP_OKAY;
    262}
    263
    264
    265/** feasibility check method of constraint handler for integral solutions.
    266 * The feasibility check for Benders' decomposition is performed in cons_benders. As such, it is redundant to perform
    267 * the feasibility check here. As such, the solution is flagged as feasible, which will then be corrected in
    268 * cons_benders if the solution is infeasible with respect to the second stage constraints
    269 */
    270static
    271SCIP_DECL_CONSCHECK(consCheckBenderslp)
    272{ /*lint --e{715}*/
    273 assert(result != NULL);
    274
    275 *result = SCIP_FEASIBLE;
    276
    277 return SCIP_OKAY;
    278}
    279
    280
    281/** variable rounding lock method of constraint handler */
    282static
    283SCIP_DECL_CONSLOCK(consLockBenderslp)
    284{ /*lint --e{715}*/
    285 return SCIP_OKAY;
    286}
    287
    288
    289
    290/*
    291 * constraint specific interface methods
    292 */
    293
    294/** creates the handler for executing the Benders' decomposition subproblem solve on fractional LP solution and
    295 * includes it in SCIP */
    297 SCIP* scip /**< SCIP data structure */
    298 )
    299{
    300 SCIP_CONSHDLRDATA* conshdlrdata = NULL;
    301 SCIP_CONSHDLR* conshdlr;
    302
    303 /* create benderslp constraint handler data */
    304 SCIP_CALL( SCIPallocMemory(scip, &conshdlrdata) );
    305 BMSclearMemory(conshdlrdata);
    306 conshdlrdata->prevbound = -SCIPinfinity(scip);
    307
    308 conshdlr = NULL;
    309
    310 /* include constraint handler */
    313 consEnfolpBenderslp, consEnfopsBenderslp, consCheckBenderslp, consLockBenderslp,
    314 conshdlrdata) );
    315 assert(conshdlr != NULL);
    316
    317 /* set non-fundamental callbacks via specific setter functions */
    318 SCIP_CALL( SCIPsetConshdlrInit(scip, conshdlr, consInitBenderslp) );
    319 SCIP_CALL( SCIPsetConshdlrExit(scip, conshdlr, consExitBenderslp) );
    320 SCIP_CALL( SCIPsetConshdlrCopy(scip, conshdlr, conshdlrCopyBenderslp, NULL) );
    321 SCIP_CALL( SCIPsetConshdlrFree(scip, conshdlr, consFreeBenderslp) );
    322 SCIP_CALL( SCIPsetConshdlrEnforelax(scip, conshdlr, consEnforelaxBenderslp) );
    323
    324 /* add Benders' decomposition LP constraint handler parameters */
    326 "constraints/" CONSHDLR_NAME "/maxdepth",
    327 "depth at which Benders' decomposition cuts are generated from the LP solution (-1: always, 0: only at root)",
    328 &conshdlrdata->maxdepth, TRUE, DEFAULT_CONSBENDERSLP_MAXDEPTH, -1, SCIP_MAXTREEDEPTH, NULL, NULL) );
    329
    331 "constraints/" CONSHDLR_NAME "/depthfreq",
    332 "the depth frequency for generating LP cuts after the max depth is reached (0: never, 1: all nodes, ...)",
    333 &conshdlrdata->freq, TRUE, DEFAULT_CONSBENDERSLP_FREQ, 0, SCIP_MAXTREEDEPTH, NULL, NULL) );
    334
    336 "constraints/" CONSHDLR_NAME "/stalllimit",
    337 "the number of nodes processed without a dual bound improvement before enforcing the LP relaxation, 0: no stall count applied",
    338 &conshdlrdata->stalllimit, TRUE, DEFAULT_CONSBENDERSLP_STALLLIMIT, 0, INT_MAX, NULL, NULL) );
    339
    341 "constraints/" CONSHDLR_NAME "/iterlimit",
    342 "after the root node, only iterlimit fractional LP solutions are used at each node to generate Benders' decomposition cuts.",
    343 &conshdlrdata->iterlimit, TRUE, DEFAULT_CONSBENDERSLP_ITERLIMIT, 0, INT_MAX, NULL, NULL) );
    344
    346 "constraints/" CONSHDLR_NAME "/active", "is the Benders' decomposition LP cut constraint handler active?",
    347 &conshdlrdata->active, FALSE, DEFAULT_ACTIVE, NULL, NULL));
    348
    349 conshdlrdata->stallcount = 0;
    350 return SCIP_OKAY;
    351}
    static GRAPHNODE ** active
    constraint handler for Benders' decomposition
    static SCIP_DECL_CONSFREE(consFreeBenderslp)
    static SCIP_DECL_CONSLOCK(consLockBenderslp)
    #define CONSHDLR_NEEDSCONS
    static SCIP_DECL_CONSENFOPS(consEnfopsBenderslp)
    #define CONSHDLR_CHECKPRIORITY
    #define CONSHDLR_DESC
    static SCIP_DECL_CONSEXIT(consExitBenderslp)
    static SCIP_DECL_CONSCHECK(consCheckBenderslp)
    static SCIP_DECL_CONSENFOLP(consEnfolpBenderslp)
    #define DEFAULT_CONSBENDERSLP_ITERLIMIT
    static SCIP_DECL_CONSINIT(consInitBenderslp)
    #define DEFAULT_CONSBENDERSLP_STALLLIMIT
    #define DEFAULT_CONSBENDERSLP_MAXDEPTH
    static SCIP_DECL_CONSHDLRCOPY(conshdlrCopyBenderslp)
    static SCIP_DECL_CONSENFORELAX(consEnforelaxBenderslp)
    #define CONSHDLR_EAGERFREQ
    #define CONSHDLR_ENFOPRIORITY
    #define CONSHDLR_NAME
    #define DEFAULT_ACTIVE
    #define DEFAULT_CONSBENDERSLP_FREQ
    constraint handler for benderslp decomposition
    #define NULL
    Definition: def.h:248
    #define SCIP_Longint
    Definition: def.h:141
    #define SCIP_MAXTREEDEPTH
    Definition: def.h:297
    #define SCIP_Bool
    Definition: def.h:91
    #define SCIP_Real
    Definition: def.h:156
    #define TRUE
    Definition: def.h:93
    #define FALSE
    Definition: def.h:94
    #define SCIP_CALL(x)
    Definition: def.h:355
    SCIP_RETCODE SCIPconsBendersEnforceSolution(SCIP *scip, SCIP_SOL *sol, SCIP_CONSHDLR *conshdlr, SCIP_RESULT *result, SCIP_BENDERSENFOTYPE type, SCIP_Bool checkint)
    Definition: cons_benders.c:286
    SCIP_RETCODE SCIPincludeConshdlrBenderslp(SCIP *scip)
    int SCIPgetSubscipDepth(SCIP *scip)
    Definition: scip_copy.c:2588
    SCIP_RETCODE SCIPaddIntParam(SCIP *scip, const char *name, const char *desc, int *valueptr, SCIP_Bool isadvanced, int defaultvalue, int minvalue, int maxvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
    Definition: scip_param.c:83
    SCIP_RETCODE SCIPaddBoolParam(SCIP *scip, const char *name, const char *desc, SCIP_Bool *valueptr, SCIP_Bool isadvanced, SCIP_Bool defaultvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
    Definition: scip_param.c:57
    SCIP_RETCODE SCIPsetConshdlrInit(SCIP *scip, SCIP_CONSHDLR *conshdlr, SCIP_DECL_CONSINIT((*consinit)))
    Definition: scip_cons.c:396
    SCIP_RETCODE SCIPincludeConshdlrBasic(SCIP *scip, SCIP_CONSHDLR **conshdlrptr, const char *name, const char *desc, int enfopriority, int chckpriority, int eagerfreq, SCIP_Bool needscons, SCIP_DECL_CONSENFOLP((*consenfolp)), SCIP_DECL_CONSENFOPS((*consenfops)), SCIP_DECL_CONSCHECK((*conscheck)), SCIP_DECL_CONSLOCK((*conslock)), SCIP_CONSHDLRDATA *conshdlrdata)
    Definition: scip_cons.c:181
    SCIP_RETCODE SCIPsetConshdlrFree(SCIP *scip, SCIP_CONSHDLR *conshdlr, SCIP_DECL_CONSFREE((*consfree)))
    Definition: scip_cons.c:372
    SCIP_RETCODE SCIPsetConshdlrEnforelax(SCIP *scip, SCIP_CONSHDLR *conshdlr, SCIP_DECL_CONSENFORELAX((*consenforelax)))
    Definition: scip_cons.c:323
    SCIP_RETCODE SCIPsetConshdlrExit(SCIP *scip, SCIP_CONSHDLR *conshdlr, SCIP_DECL_CONSEXIT((*consexit)))
    Definition: scip_cons.c:420
    SCIP_Bool SCIPconshdlrNeedsCons(SCIP_CONSHDLR *conshdlr)
    Definition: cons.c:5302
    SCIP_RETCODE SCIPsetConshdlrCopy(SCIP *scip, SCIP_CONSHDLR *conshdlr, SCIP_DECL_CONSHDLRCOPY((*conshdlrcopy)), SCIP_DECL_CONSCOPY((*conscopy)))
    Definition: scip_cons.c:347
    void SCIPconshdlrSetNeedsCons(SCIP_CONSHDLR *conshdlr, SCIP_Bool needscons)
    Definition: cons.c:5312
    SCIP_CONSHDLRDATA * SCIPconshdlrGetData(SCIP_CONSHDLR *conshdlr)
    Definition: cons.c:4336
    #define SCIPallocMemory(scip, ptr)
    Definition: scip_mem.h:60
    #define SCIPfreeMemory(scip, ptr)
    Definition: scip_mem.h:78
    SCIP_Real SCIPgetLowerbound(SCIP *scip)
    SCIP_Real SCIPinfinity(SCIP *scip)
    SCIP_Bool SCIPisLT(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
    int SCIPgetDepth(SCIP *scip)
    Definition: scip_tree.c:672
    SCIP_NODE * SCIPgetCurrentNode(SCIP *scip)
    Definition: scip_tree.c:91
    #define BMSclearMemory(ptr)
    Definition: memory.h:129
    SCIP callable library.
    @ SCIP_BENDERSENFOTYPE_RELAX
    Definition: type_benders.h:52
    @ SCIP_BENDERSENFOTYPE_LP
    Definition: type_benders.h:51
    @ SCIP_BENDERSENFOTYPE_PSEUDO
    Definition: type_benders.h:53
    struct SCIP_ConshdlrData SCIP_CONSHDLRDATA
    Definition: type_cons.h:64
    @ SCIP_FEASIBLE
    Definition: type_result.h:45
    @ SCIP_OKAY
    Definition: type_retcode.h:42
    enum SCIP_Retcode SCIP_RETCODE
    Definition: type_retcode.h:63