1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /*                                                                           */
3 /*                  This file is part of the program and library             */
4 /*         SCIP --- Solving Constraint Integer Programs                      */
5 /*                                                                           */
6 /*    Copyright (C) 2002-2021 Konrad-Zuse-Zentrum                            */
7 /*                            fuer Informationstechnik Berlin                */
8 /*                                                                           */
9 /*  SCIP is distributed under the terms of the ZIB Academic License.         */
10 /*                                                                           */
11 /*  You should have received a copy of the ZIB Academic License              */
12 /*  along with SCIP; see the file COPYING. If not visit scipopt.org.         */
13 /*                                                                           */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15 
16 /**@file   benderscut_feasalt.c
17  * @brief  Alternative feasibility cuts for Benders' decomposition
18  * @author Stephen J. Maher
19  */
20 
21 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
22 
23 #include <assert.h>
24 #include <string.h>
25 
26 #include "nlpi/exprinterpret.h"
27 #include "nlpi/pub_expr.h"
28 #include "nlpi/nlpi.h"
29 #include "scip/benderscut_feasalt.h"
30 #include "scip/benderscut_opt.h"
31 #include "scip/cons_linear.h"
32 #include "scip/pub_benderscut.h"
33 #include "scip/pub_benders.h"
34 #include "scip/pub_lp.h"
35 #include "scip/pub_message.h"
36 #include "scip/pub_misc.h"
37 #include "scip/pub_misc_linear.h"
38 #include "scip/pub_nlp.h"
39 #include "scip/pub_var.h"
40 #include "scip/scip_benders.h"
41 #include "scip/scip_cons.h"
42 #include "scip/scip_general.h"
43 #include "scip/scip_lp.h"
44 #include "scip/scip_mem.h"
45 #include "scip/scip_message.h"
46 #include "scip/scip_nlp.h"
47 #include "scip/scip_nonlinear.h"
48 #include "scip/scip_numerics.h"
49 #include "scip/scip_param.h"
50 #include "scip/scip_prob.h"
51 #include "scip/scip_solvingstats.h"
52 #include "scip/scip_timing.h"
53 #include "scip/scip_var.h"
54 
55 #define BENDERSCUT_NAME             "feasalt"
56 #define BENDERSCUT_DESC             "Alternative feasibility cuts for Benders' decomposition"
57 #define BENDERSCUT_PRIORITY     10001
58 #define BENDERSCUT_LPCUT         TRUE
59 
60 #define SCIP_DEFAULT_DISPLAYFREQ 20
61 #define SLACKVAR_NAME    "##bendersslackvar" /** the name for the Benders' slack variables added to each
62                                               *  constraints in the subproblems */
63 
64 struct SCIP_BenderscutData
65 {
66    SCIP_NLPI*            nlpi;               /**< nlpi used to create the nlpi problem */
67    SCIP_NLPIPROBLEM*     nlpiprob;           /**< nlpi problem representing the convex NLP relaxation */
68    SCIP_HASHMAP*         var2idx;            /**< mapping the variable to the index in the NLPI problem */
69    SCIP_HASHMAP*         row2idx;            /**< mapping the rows to the index in the NLPI problem */
70    SCIP_VAR**            nlpivars;           /**< the variables in the NLPI problem */
71    SCIP_NLROW**          nlpirows;           /**< the rows in the NLPI problem */
72    int                   nlpinvars;          /**< the number of variables in the NPLI problem */
73    int                   nlpinrows;          /**< the number of rows in the NLPI problem */
74    int                   nlpinslackvars;     /**< the number of slack variables in the NLPI problem */
75    int                   nlpiprobsubprob;    /**< the index of the subproblem that the nonlinear problem belongs to */
76 
77    SCIP_Real*            slackvarlbs;        /**< an array of zeros for the slack variable lower bounds*/
78    SCIP_Real*            slackvarubs;        /**< an array of infinity for the slack variable upper bounds*/
79    int*                  slackvarinds;       /**< array of indices for the slack variables */
80 };
81 
82 /*
83  * Local methods
84  */
85 
86 /** frees the non linear problem */
87 static
freeNonlinearProblem(SCIP * scip,SCIP_BENDERSCUT * benderscut)88 SCIP_RETCODE freeNonlinearProblem(
89    SCIP*                 scip,               /**< the SCIP data structure */
90    SCIP_BENDERSCUT*      benderscut          /**< the Benders' decomposition structure */
91    )
92 {
93    SCIP_BENDERSCUTDATA* benderscutdata;
94 
95    assert(scip != NULL);
96    assert(benderscut != NULL);
97 
98    benderscutdata = SCIPbenderscutGetData(benderscut);
99    assert(benderscutdata != NULL);
100 
101    if( benderscutdata->nlpiprob != NULL )
102    {
103       assert(benderscutdata->nlpi != NULL);
104 
105       SCIPfreeBlockMemoryArray(scip, &benderscutdata->slackvarinds, benderscutdata->nlpinvars);
106       SCIPfreeBlockMemoryArray(scip, &benderscutdata->slackvarubs, benderscutdata->nlpinvars);
107       SCIPfreeBlockMemoryArray(scip, &benderscutdata->slackvarlbs, benderscutdata->nlpinvars);
108       SCIPfreeBlockMemoryArray(scip, &benderscutdata->nlpirows, benderscutdata->nlpinrows);
109       SCIPfreeBlockMemoryArray(scip, &benderscutdata->nlpivars, benderscutdata->nlpinvars);
110       SCIPhashmapFree(&benderscutdata->row2idx);
111       SCIPhashmapFree(&benderscutdata->var2idx);
112 
113       SCIP_CALL( SCIPnlpiFreeProblem(benderscutdata->nlpi, &benderscutdata->nlpiprob) );
114 
115       benderscutdata->nlpinslackvars = 0;
116       benderscutdata->nlpinrows = 0;
117       benderscutdata->nlpinvars = 0;
118 
119       benderscutdata->nlpi = NULL;
120    }
121 
122    return SCIP_OKAY;
123 }
124 
125 /** solves the auxiliary feasibility subproblem.
126  *
127  *  @note: the variable fixings need to be setup before calling this function
128  */
129 static
solveFeasibilityNonlinearSubproblem(SCIP * scip,SCIP_BENDERSCUTDATA * benderscutdata,SCIP_Bool * success)130 SCIP_RETCODE solveFeasibilityNonlinearSubproblem(
131    SCIP*                 scip,               /**< SCIP data structure */
132    SCIP_BENDERSCUTDATA*  benderscutdata,     /**< Benders' cut data */
133    SCIP_Bool*            success             /**< returns whether solving the feasibility problem was successful */
134    )
135 {
136    SCIP_Real timelimit;
137    SCIP_NLPSOLSTAT nlpsolstat;
138 
139    assert(scip != NULL);
140    assert(benderscutdata != NULL);
141 
142    (*success) = TRUE;
143 
144    /* setting the time limit */
145    SCIP_CALL( SCIPgetRealParam(scip, "limits/time", &timelimit) );
146    if( !SCIPisInfinity(scip, timelimit) )
147    {
148       timelimit -= SCIPgetSolvingTime(scip);
149       if( timelimit <= 0.0 )
150       {
151          SCIPdebugMsg(scip, "skip NLP solve; no time left\n");
152          return SCIP_OKAY;
153       }
154    }
155    SCIP_CALL( SCIPnlpiSetRealPar(benderscutdata->nlpi, benderscutdata->nlpiprob, SCIP_NLPPAR_TILIM, timelimit) );
156 
157 #ifdef SCIP_MOREDEBUG
158       SCIP_CALL( SCIPnlpiSetIntPar(benderscutdata->nlpi, benderscutdata->nlpiprob, SCIP_NLPPAR_VERBLEVEL, 1) );
159 #endif
160 
161    SCIP_CALL( SCIPnlpiSolve(benderscutdata->nlpi, benderscutdata->nlpiprob) );
162    SCIPdebugMsg(scip, "NLP solstat = %d\n", SCIPnlpiGetSolstat(benderscutdata->nlpi, benderscutdata->nlpiprob));
163 
164    nlpsolstat = SCIPnlpiGetSolstat(benderscutdata->nlpi, benderscutdata->nlpiprob);
165 
166    /* if the feasibility NLP is not feasible, then it is not possible to generate a Benders' cut. This is also an error,
167     * since the NLP should always be feasible. In debug mode, an ABORT will be thrown.
168     */
169    if( nlpsolstat > SCIP_NLPSOLSTAT_FEASIBLE )
170       (*success) = FALSE;
171 
172    return SCIP_OKAY;
173 }
174 
175 /** builds the non-linear problem to resolve to generate a cut for the infeasible subproblem */
176 static
createAuxiliaryNonlinearSubproblem(SCIP * masterprob,SCIP * subproblem,SCIP_BENDERSCUT * benderscut)177 SCIP_RETCODE createAuxiliaryNonlinearSubproblem(
178    SCIP*                 masterprob,         /**< the SCIP instance of the master problem */
179    SCIP*                 subproblem,         /**< the SCIP instance of the pricing problem */
180    SCIP_BENDERSCUT*      benderscut          /**< the benders' decomposition cut method */
181    )
182 {
183    SCIP_BENDERSCUTDATA* benderscutdata;
184    SCIP_Real* obj;
185    int i;
186 
187    assert(masterprob != NULL);
188 
189    benderscutdata = SCIPbenderscutGetData(benderscut);
190    assert(benderscutdata != NULL);
191 
192    /* first freeing the non-linear problem if it exists */
193    SCIP_CALL( freeNonlinearProblem(masterprob, benderscut) );
194 
195    assert(benderscutdata->nlpi == NULL);
196    assert(benderscutdata->nlpiprob == NULL);
197 
198    benderscutdata->nlpinvars = SCIPgetNVars(subproblem);
199    benderscutdata->nlpinrows = SCIPgetNNLPNlRows(subproblem);
200    benderscutdata->nlpi = SCIPgetNlpis(subproblem)[0];
201    assert(benderscutdata->nlpi != NULL);
202 
203    SCIP_CALL( SCIPnlpiCreateProblem(benderscutdata->nlpi, &benderscutdata->nlpiprob, "benders-feascutalt-nlp") );
204    SCIP_CALL( SCIPhashmapCreate(&benderscutdata->var2idx, SCIPblkmem(masterprob), benderscutdata->nlpinvars) );
205    SCIP_CALL( SCIPhashmapCreate(&benderscutdata->row2idx, SCIPblkmem(masterprob), benderscutdata->nlpinrows) );
206 
207    SCIP_CALL( SCIPduplicateBlockMemoryArray(masterprob, &benderscutdata->nlpivars, SCIPgetVars(subproblem),
208          benderscutdata->nlpinvars) ); /*lint !e666*/
209    SCIP_CALL( SCIPduplicateBlockMemoryArray(masterprob, &benderscutdata->nlpirows, SCIPgetNLPNlRows(subproblem),
210          benderscutdata->nlpinrows) ); /*lint !e666*/
211 
212    SCIP_CALL( SCIPcreateNlpiProb(subproblem, benderscutdata->nlpi, SCIPgetNLPNlRows(subproblem), benderscutdata->nlpinrows,
213          benderscutdata->nlpiprob, benderscutdata->var2idx, benderscutdata->row2idx, NULL, SCIPinfinity(subproblem), FALSE,
214          FALSE) );
215 
216    /* storing the slack variable bounds and indices */
217    SCIP_CALL( SCIPallocBufferArray(masterprob, &obj, benderscutdata->nlpinvars) );
218 
219    SCIP_CALL( SCIPallocBlockMemoryArray(masterprob, &benderscutdata->slackvarlbs, benderscutdata->nlpinvars) );
220    SCIP_CALL( SCIPallocBlockMemoryArray(masterprob, &benderscutdata->slackvarubs, benderscutdata->nlpinvars) );
221    SCIP_CALL( SCIPallocBlockMemoryArray(masterprob, &benderscutdata->slackvarinds, benderscutdata->nlpinvars) );
222    benderscutdata->nlpinslackvars = 0;
223    for( i = 0; i < benderscutdata->nlpinvars; i++ )
224    {
225       if( strstr(SCIPvarGetName(benderscutdata->nlpivars[i]), SLACKVAR_NAME) )
226       {
227          benderscutdata->slackvarlbs[benderscutdata->nlpinslackvars] = 0.0;
228          benderscutdata->slackvarubs[benderscutdata->nlpinslackvars] = SCIPinfinity(subproblem);
229          benderscutdata->slackvarinds[benderscutdata->nlpinslackvars] = SCIPhashmapGetImageInt(benderscutdata->var2idx,
230             (void*)benderscutdata->nlpivars[i]);
231 
232          obj[benderscutdata->nlpinslackvars] = 1.0;
233 
234          benderscutdata->nlpinslackvars++;
235       }
236    }
237 
238    /* setting the objective function */
239    SCIP_CALL( SCIPnlpiSetObjective(benderscutdata->nlpi, benderscutdata->nlpiprob, benderscutdata->nlpinslackvars,
240          benderscutdata->slackvarinds, obj, 0, NULL, NULL, NULL, 0.0) );
241 
242    /* unfixing the slack variables */
243    SCIP_CALL( SCIPnlpiChgVarBounds(benderscutdata->nlpi, benderscutdata->nlpiprob, benderscutdata->nlpinslackvars,
244          benderscutdata->slackvarinds, benderscutdata->slackvarlbs, benderscutdata->slackvarubs) );
245 
246    SCIPfreeBufferArray(masterprob, &obj);
247 
248    return SCIP_OKAY;
249 }
250 
251 /** updates the non-linear problem that is resolved to generate a cut for the infeasible subproblem */
252 static
updateAuxiliaryNonlinearSubproblem(SCIP * subproblem,SCIP_BENDERSCUT * benderscut)253 SCIP_RETCODE updateAuxiliaryNonlinearSubproblem(
254    SCIP*                 subproblem,         /**< the SCIP instance of the pricing problem */
255    SCIP_BENDERSCUT*      benderscut          /**< the benders' decomposition cut method */
256    )
257 {
258    SCIP_BENDERSCUTDATA* benderscutdata;
259 
260    assert(subproblem != NULL);
261    assert(benderscut != NULL);
262 
263    benderscutdata = SCIPbenderscutGetData(benderscut);
264    assert(benderscutdata != NULL);
265    assert(benderscutdata->nlpi != NULL);
266    assert(benderscutdata->nlpiprob != NULL);
267    assert(benderscutdata->var2idx != NULL);
268    assert(benderscutdata->row2idx != NULL);
269 
270    /* setting the variable bounds to that from the current subproblem */
271    SCIP_CALL( SCIPupdateNlpiProb(subproblem, benderscutdata->nlpi, benderscutdata->nlpiprob, benderscutdata->var2idx,
272          benderscutdata->nlpivars, benderscutdata->nlpinvars, SCIPinfinity(subproblem)) );
273 
274    /* unfixing the slack variables */
275    SCIP_CALL( SCIPnlpiChgVarBounds(benderscutdata->nlpi, benderscutdata->nlpiprob, benderscutdata->nlpinslackvars,
276          benderscutdata->slackvarinds, benderscutdata->slackvarlbs, benderscutdata->slackvarubs) );
277 
278    return SCIP_OKAY;
279 }
280 
281 /** generates and applies Benders' cuts */
282 static
generateAndApplyBendersCuts(SCIP * masterprob,SCIP * subproblem,SCIP_BENDERS * benders,SCIP_BENDERSCUT * benderscut,SCIP_SOL * sol,int probnumber,SCIP_BENDERSENFOTYPE type,SCIP_RESULT * result)283 SCIP_RETCODE generateAndApplyBendersCuts(
284    SCIP*                 masterprob,         /**< the SCIP instance of the master problem */
285    SCIP*                 subproblem,         /**< the SCIP instance of the pricing problem */
286    SCIP_BENDERS*         benders,            /**< the benders' decomposition */
287    SCIP_BENDERSCUT*      benderscut,         /**< the benders' decomposition cut method */
288    SCIP_SOL*             sol,                /**< primal CIP solution */
289    int                   probnumber,         /**< the number of the pricing problem */
290    SCIP_BENDERSENFOTYPE  type,               /**< the enforcement type calling this function */
291    SCIP_RESULT*          result              /**< the result from solving the subproblems */
292    )
293 {
294    SCIP_BENDERSCUTDATA* benderscutdata;
295    SCIP_Real* primalvals;
296    SCIP_Real* consdualvals;
297    SCIP_Real* varlbdualvals;
298    SCIP_Real* varubdualvals;
299    SCIP_Real obj;
300    char cutname[SCIP_MAXSTRLEN];
301    SCIP_Bool success;
302 #ifdef SCIP_EVENMOREDEBUG
303    int i;
304 #endif
305 
306    assert(masterprob != NULL);
307    assert(subproblem != NULL);
308    assert(benders != NULL);
309    assert(result != NULL);
310 
311    benderscutdata = SCIPbenderscutGetData(benderscut);
312    assert(benderscutdata != NULL);
313 
314    /* creating or updating the NLPI problem */
315    if( benderscutdata->nlpiprob == NULL || benderscutdata->nlpiprobsubprob != probnumber )
316    {
317       SCIP_CALL( createAuxiliaryNonlinearSubproblem(masterprob, subproblem, benderscut) );
318       benderscutdata->nlpiprobsubprob = probnumber;
319    }
320    else
321    {
322       SCIP_CALL( updateAuxiliaryNonlinearSubproblem(subproblem, benderscut) );
323    }
324 
325    /* solving the NLPI problem to get the minimum infeasible solution */
326    SCIP_CALL( solveFeasibilityNonlinearSubproblem(subproblem, benderscutdata, &success) );
327 
328    if( !success )
329    {
330       (*result) = SCIP_DIDNOTFIND;
331       SCIPdebugMsg(masterprob, "Error in generating Benders' feasibility cut for problem %d. "
332          "The feasibility subproblem failed to solve with a feasible solution.\n", probnumber);
333       return SCIP_OKAY;
334    }
335 
336    /* getting the solution from the NLPI problem */
337    SCIP_CALL( SCIPnlpiGetSolution(benderscutdata->nlpi, benderscutdata->nlpiprob, &primalvals, &consdualvals,
338          &varlbdualvals, &varubdualvals, &obj) );
339 
340 #ifdef SCIP_EVENMOREDEBUG
341    SCIPdebugMsg(masterprob, "NLP Feasibility problem solution.\n");
342    SCIPdebugMsg(masterprob, "Objective: %g.\n", obj);
343    for( i = 0; i < benderscutdata->nlpinvars; i++ )
344    {
345       int varindex;
346       SCIP_Real solval;
347       if( SCIPhashmapExists(benderscutdata->var2idx, benderscutdata->nlpivars[i]) )
348       {
349          varindex = SCIPhashmapGetImageInt(benderscutdata->var2idx, benderscutdata->nlpivars[i]);
350          solval = primalvals[varindex];
351 
352          if( !SCIPisZero(masterprob, solval) )
353          {
354             SCIPdebugMsg(masterprob, "%s (obj: %g): %20g\n", SCIPvarGetName(benderscutdata->nlpivars[i]),
355                SCIPvarGetObj(benderscutdata->nlpivars[i]), solval);
356          }
357       }
358    }
359 #endif
360 
361    /* setting the name of the generated cut */
362    (void) SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "altfeasibilitycut_%d_%" SCIP_LONGINT_FORMAT, probnumber,
363       SCIPbenderscutGetNFound(benderscut) );
364 
365    /* generating a Benders' decomposition cut using the classical optimality cut methods */
366    SCIP_CALL( SCIPgenerateAndApplyBendersOptCut(masterprob, subproblem, benders, benderscut,
367          sol, probnumber, cutname, obj, primalvals, consdualvals, varlbdualvals, varubdualvals, benderscutdata->row2idx,
368          benderscutdata->var2idx, type, FALSE, TRUE, result) );
369 
370    if( (*result) == SCIP_CONSADDED )
371    {
372       if( SCIPisInfinity(masterprob, -SCIPgetDualbound(masterprob))
373          && SCIPbenderscutGetNFound(benderscut) % SCIP_DEFAULT_DISPLAYFREQ == 0 )
374       {
375          if( SCIPgetStage(masterprob) == SCIP_STAGE_SOLVING )
376          {
377             SCIP_CALL( SCIPprintDisplayLine(masterprob, NULL, SCIP_VERBLEVEL_NORMAL, TRUE) );
378             SCIPverbMessage(masterprob, SCIP_VERBLEVEL_NORMAL, NULL,
379                "Benders' Decomposition: Master problem LP is infeasible. Added %" SCIP_LONGINT_FORMAT " feasibility cuts.\n",
380                SCIPbenderscutGetNFound(benderscut));
381          }
382       }
383       SCIPdebugMsg(masterprob, "Constraints %s has been added to the master problem.\n", cutname);
384    }
385 
386    return SCIP_OKAY;
387 }
388 
389 /*
390  * Callback methods of Benders' decomposition cuts
391  */
392 
393 /** deinitialization method of Benders' decomposition cuts (called before transformed problem is freed) */
394 static
SCIP_DECL_BENDERSCUTEXIT(benderscutExitFeasalt)395 SCIP_DECL_BENDERSCUTEXIT(benderscutExitFeasalt)
396 {  /*lint --e{715}*/
397    assert( benderscut != NULL );
398    assert( strcmp(SCIPbenderscutGetName(benderscut), BENDERSCUT_NAME) == 0 );
399 
400    /* freeing the non-linear problem information */
401    SCIP_CALL( freeNonlinearProblem(scip, benderscut) );
402 
403    return SCIP_OKAY;
404 }
405 
406 /** destructor of the Benders' decomposition cut to free user data (called when SCIP is exiting) */
407 static
SCIP_DECL_BENDERSCUTFREE(benderscutFreeFeasalt)408 SCIP_DECL_BENDERSCUTFREE(benderscutFreeFeasalt)
409 {  /*lint --e{715}*/
410    SCIP_BENDERSCUTDATA* benderscutdata;
411 
412    assert(scip != NULL);
413    assert(benderscut != NULL);
414 
415    benderscutdata = SCIPbenderscutGetData(benderscut);
416    assert(benderscutdata != NULL);
417 
418    SCIPfreeBlockMemory(scip, &benderscutdata);
419 
420    return SCIP_OKAY;
421 }
422 
423 /** execution method of Benders' decomposition cuts */
424 static
SCIP_DECL_BENDERSCUTEXEC(benderscutExecFeasalt)425 SCIP_DECL_BENDERSCUTEXEC(benderscutExecFeasalt)
426 {  /*lint --e{715}*/
427    SCIP* subproblem;
428    SCIP_Bool nlprelaxation;
429 
430    assert(scip != NULL);
431    assert(benders != NULL);
432    assert(benderscut != NULL);
433    assert(result != NULL);
434    assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders));
435 
436    subproblem = SCIPbendersSubproblem(benders, probnumber);
437 
438    /* setting a flag to indicate whether the NLP relaxation should be used to generate cuts */
439    nlprelaxation = SCIPisNLPConstructed(subproblem) && SCIPgetNNlpis(subproblem)
440       && SCIPbendersGetSubproblemType(benders, probnumber) <= SCIP_BENDERSSUBTYPE_CONVEXDIS;
441 
442    /* only generate feasibility cuts if the subproblem LP or NLP is infeasible,
443     * since we use the farkas proof from the LP or the dual solution of the NLP to construct the feasibility cut
444     */
445    if( SCIPgetStage(subproblem) == SCIP_STAGE_SOLVING &&
446        (nlprelaxation && (SCIPgetNLPSolstat(subproblem) == SCIP_NLPSOLSTAT_LOCINFEASIBLE || SCIPgetNLPSolstat(subproblem) == SCIP_NLPSOLSTAT_GLOBINFEASIBLE)) )
447    {
448       /* generating a cut for a given subproblem */
449       SCIP_CALL( generateAndApplyBendersCuts(scip, subproblem, benders, benderscut, sol, probnumber, type, result) );
450    }
451 
452    return SCIP_OKAY;
453 }
454 
455 
456 /*
457  * Benders' decomposition cuts specific interface methods
458  */
459 
460 /** creates the Alternative Feasibility Benders' decomposition cuts and includes it in SCIP */
SCIPincludeBenderscutFeasalt(SCIP * scip,SCIP_BENDERS * benders)461 SCIP_RETCODE SCIPincludeBenderscutFeasalt(
462    SCIP*                 scip,               /**< SCIP data structure */
463    SCIP_BENDERS*         benders             /**< Benders' decomposition */
464    )
465 {
466    SCIP_BENDERSCUT* benderscut;
467    SCIP_BENDERSCUTDATA* benderscutdata;
468 
469    assert(benders != NULL);
470 
471    benderscut = NULL;
472 
473    SCIP_CALL( SCIPallocBlockMemory(scip, &benderscutdata) );
474    BMSclearMemory(benderscutdata);
475    benderscutdata->nlpiprobsubprob = -1;
476 
477    /* include Benders' decomposition cuts */
478    SCIP_CALL( SCIPincludeBenderscutBasic(scip, benders, &benderscut, BENDERSCUT_NAME, BENDERSCUT_DESC,
479          BENDERSCUT_PRIORITY, BENDERSCUT_LPCUT, benderscutExecFeasalt, benderscutdata) );
480 
481    /* set non fundamental callbacks via setter functions */
482    SCIP_CALL( SCIPsetBenderscutFree(scip, benderscut, benderscutFreeFeasalt) );
483    SCIP_CALL( SCIPsetBenderscutExit(scip, benderscut, benderscutExitFeasalt) );
484 
485    assert(benderscut != NULL);
486 
487    return SCIP_OKAY;
488 }
489