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   scip/src/scip/benders.c
17  * @ingroup OTHER_CFILES
18  * @brief  methods for Benders' decomposition
19  * @author Stephen J. Maher
20  */
21 
22 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
23 
24 #include <assert.h>
25 #include <string.h>
26 
27 #include "scip/def.h"
28 #include "scip/set.h"
29 #include "scip/clock.h"
30 #include "scip/dcmp.h"
31 #include "scip/paramset.h"
32 #include "scip/lp.h"
33 #include "scip/prob.h"
34 #include "scip/pricestore.h"
35 #include "scip/scip.h"
36 #include "scip/scipdefplugins.h"
37 #include "scip/benders.h"
38 #include "scip/pub_message.h"
39 #include "scip/pub_misc.h"
40 #include "scip/pub_misc_linear.h"
41 #include "scip/pub_misc_nonlinear.h"
42 #include "scip/cons_linear.h"
43 #include "scip/cons_nonlinear.h"
44 #include "scip/cons_quadratic.h"
45 #include "scip/cons_abspower.h"
46 
47 #include "scip/struct_benders.h"
48 #include "scip/struct_benderscut.h"
49 
50 #include "scip/benderscut.h"
51 
52 /* Defaults for parameters */
53 #define SCIP_DEFAULT_TRANSFERCUTS         FALSE  /** should Benders' cuts generated in LNS heuristics be transferred to the main SCIP instance? */
54 #define SCIP_DEFAULT_CUTSASCONSS           TRUE  /** should the transferred cuts be added as constraints? */
55 #define SCIP_DEFAULT_LNSCHECK              TRUE  /** should the Benders' decomposition be used in LNS heuristics */
56 #define SCIP_DEFAULT_LNSMAXDEPTH             -1  /** maximum depth at which the LNS check is performed */
57 #define SCIP_DEFAULT_LNSMAXCALLS             10  /** the maximum number of Benders' decomposition calls in LNS heuristics */
58 #define SCIP_DEFAULT_LNSMAXCALLSROOT          0  /** the maximum number of root node Benders' decomposition calls in LNS heuristics */
59 #define SCIP_DEFAULT_SUBPROBFRAC            1.0  /** fraction of subproblems that are solved in each iteration */
60 #define SCIP_DEFAULT_UPDATEAUXVARBOUND    FALSE  /** should the auxiliary variable lower bound be updated by solving the subproblem */
61 #define SCIP_DEFAULT_AUXVARSIMPLINT       FALSE  /** set the auxiliary variables as implint if the subproblem objective is integer */
62 #define SCIP_DEFAULT_CUTCHECK              TRUE  /** should cuts be generated during the checking of solutions? */
63 #define SCIP_DEFAULT_STRENGTHENMULT         0.5  /** the convex combination multiplier for the cut strengthening */
64 #define SCIP_DEFAULT_NOIMPROVELIMIT           5  /** the maximum number of cut strengthening without improvement */
65 #define SCIP_DEFAULT_STRENGTHENPERTURB    1e-06  /** the amount by which the cut strengthening solution is perturbed */
66 #define SCIP_DEFAULT_STRENGTHENENABLED    FALSE  /** enable the core point cut strengthening approach */
67 #define SCIP_DEFAULT_STRENGTHENINTPOINT     'r'  /** where should the strengthening interior point be sourced from ('l'p relaxation, 'f'irst solution, 'i'ncumbent solution, 'r'elative interior point, vector of 'o'nes, vector of 'z'eros) */
68 #define SCIP_DEFAULT_NUMTHREADS               1  /** the number of parallel threads to use when solving the subproblems */
69 #define SCIP_DEFAULT_EXECFEASPHASE        FALSE  /** should a feasibility phase be executed during the root node processing */
70 #define SCIP_DEFAULT_SLACKVARCOEF          1e+6  /** the objective coefficient of the slack variables in the subproblem */
71 #define SCIP_DEFAULT_CHECKCONSCONVEXITY    TRUE  /** should the constraints of the subproblem be checked for convexity? */
72 
73 #define BENDERS_MAXPSEUDOSOLS                 5  /** the maximum number of pseudo solutions checked before suggesting
74                                                   *  merge candidates */
75 
76 #define BENDERS_ARRAYSIZE        1000    /**< the initial size of the added constraints/cuts arrays */
77 
78 #define AUXILIARYVAR_NAME     "##bendersauxiliaryvar" /** the name for the Benders' auxiliary variables in the master problem */
79 #define SLACKVAR_NAME         "##bendersslackvar"     /** the name for the Benders' slack variables added to each
80                                                        *  constraints in the subproblems */
81 #define NLINEARCONSHDLRS 5
82 
83 /* event handler properties */
84 #define NODEFOCUS_EVENTHDLR_NAME         "bendersnodefocus"
85 #define NODEFOCUS_EVENTHDLR_DESC         "node focus event handler for Benders' decomposition"
86 
87 #define MIPNODEFOCUS_EVENTHDLR_NAME      "bendersmipsolvenodefocus"
88 #define MIPNODEFOCUS_EVENTHDLR_DESC      "node focus event handler for the MIP solve method for Benders' decomposition"
89 
90 #define UPPERBOUND_EVENTHDLR_NAME        "bendersupperbound"
91 #define UPPERBOUND_EVENTHDLR_DESC        "found solution event handler to terminate subproblem solve for a given upper bound"
92 
93 #define NODESOLVED_EVENTHDLR_NAME        "bendersnodesolved"
94 #define NODESOLVED_EVENTHDLR_DESC        "node solved event handler for the Benders' integer cuts"
95 
96 
97 /** event handler data */
98 struct SCIP_EventhdlrData
99 {
100    int                   filterpos;          /**< the event filter entry */
101    int                   numruns;            /**< the number of times that the problem has been solved */
102    SCIP_Real             upperbound;         /**< an upper bound for the problem */
103    SCIP_Bool             solvecip;           /**< is the event called from a MIP subproblem solve*/
104 };
105 
106 
107 /* ---------------- Local methods for event handlers ---------------- */
108 
109 /** initialises the members of the eventhandler data */
110 static
initEventhandlerData(SCIP * scip,SCIP_EVENTHDLRDATA * eventhdlrdata)111 SCIP_RETCODE initEventhandlerData(
112    SCIP*                 scip,               /**< the SCIP data structure */
113    SCIP_EVENTHDLRDATA*   eventhdlrdata       /**< the event handler data */
114    )
115 {
116    assert(scip != NULL);
117    assert(eventhdlrdata != NULL);
118 
119    eventhdlrdata->filterpos = -1;
120    eventhdlrdata->numruns = 0;
121    eventhdlrdata->upperbound = -SCIPinfinity(scip);
122    eventhdlrdata->solvecip = FALSE;
123 
124    return SCIP_OKAY;
125 }
126 
127 /** initsol method for the event handlers */
128 static
initsolEventhandler(SCIP * scip,SCIP_EVENTHDLR * eventhdlr,SCIP_EVENTTYPE eventtype)129 SCIP_RETCODE initsolEventhandler(
130    SCIP*                 scip,               /**< the SCIP data structure */
131    SCIP_EVENTHDLR*       eventhdlr,          /**< the event handlers data structure */
132    SCIP_EVENTTYPE        eventtype           /**< event type mask to select events to catch */
133    )
134 {
135    SCIP_EVENTHDLRDATA* eventhdlrdata;
136 
137    assert(scip != NULL);
138    assert(eventhdlr != NULL);
139 
140    eventhdlrdata = SCIPeventhdlrGetData(eventhdlr);
141 
142    SCIP_CALL(SCIPcatchEvent(scip, eventtype, eventhdlr, NULL, &eventhdlrdata->filterpos));
143 
144    return SCIP_OKAY;
145 }
146 
147 /** the exit sol method for the event handlers */
148 static
exitsolEventhandler(SCIP * scip,SCIP_EVENTHDLR * eventhdlr,SCIP_EVENTTYPE eventtype)149 SCIP_RETCODE exitsolEventhandler(
150    SCIP*                 scip,               /**< the SCIP data structure */
151    SCIP_EVENTHDLR*       eventhdlr,          /**< the event handlers data structure */
152    SCIP_EVENTTYPE        eventtype           /**< event type mask to select events to catch */
153    )
154 {
155    SCIP_EVENTHDLRDATA* eventhdlrdata;
156 
157    assert(scip != NULL);
158    assert(eventhdlr != NULL);
159 
160    eventhdlrdata = SCIPeventhdlrGetData(eventhdlr);
161 
162    if( eventhdlrdata->filterpos >= 0 )
163    {
164       SCIP_CALL(SCIPdropEvent(scip, eventtype, eventhdlr, NULL, eventhdlrdata->filterpos));
165       eventhdlrdata->filterpos = -1;
166    }
167 
168    return SCIP_OKAY;
169 }
170 
171 /** the exit method for the event handlers */
172 static
exitEventhandler(SCIP * scip,SCIP_EVENTHDLR * eventhdlr)173 SCIP_RETCODE exitEventhandler(
174    SCIP*                 scip,               /**< the SCIP data structure */
175    SCIP_EVENTHDLR*       eventhdlr           /**< the event handlers data structure */
176    )
177 {
178    SCIP_EVENTHDLRDATA* eventhdlrdata;
179 
180    assert(scip != NULL);
181    assert(eventhdlr != NULL);
182 
183    eventhdlrdata = SCIPeventhdlrGetData(eventhdlr);
184 
185    /* reinitialise the event handler data */
186    SCIP_CALL( initEventhandlerData(scip, eventhdlrdata) );
187 
188    return SCIP_OKAY;
189 }
190 
191 /** free method for the event handler */
192 static
freeEventhandler(SCIP * scip,SCIP_EVENTHDLR * eventhdlr)193 SCIP_RETCODE freeEventhandler(
194    SCIP*                 scip,               /**< the SCIP data structure */
195    SCIP_EVENTHDLR*       eventhdlr           /**< the event handlers data structure */
196    )
197 {
198    SCIP_EVENTHDLRDATA* eventhdlrdata;
199 
200    assert(scip != NULL);
201    assert(eventhdlr != NULL);
202 
203    eventhdlrdata = SCIPeventhdlrGetData(eventhdlr);
204    assert(eventhdlrdata != NULL);
205 
206    SCIPfreeBlockMemory(scip, &eventhdlrdata);
207 
208    SCIPeventhdlrSetData(eventhdlr, NULL);
209 
210    return SCIP_OKAY;
211 }
212 
213 
214 
215 /* ---------------- Callback methods of node focus event handler ---------------- */
216 
217 /** exec the event handler */
218 static
SCIP_DECL_EVENTEXEC(eventExecBendersNodefocus)219 SCIP_DECL_EVENTEXEC(eventExecBendersNodefocus)
220 {  /*lint --e{715}*/
221    SCIP_EVENTHDLRDATA* eventhdlrdata;
222 
223    assert(scip != NULL);
224    assert(eventhdlr != NULL);
225    assert(strcmp(SCIPeventhdlrGetName(eventhdlr), NODEFOCUS_EVENTHDLR_NAME) == 0);
226 
227    eventhdlrdata = SCIPeventhdlrGetData(eventhdlr);
228 
229    /* sending an interrupt solve signal to return the control back to the Benders' decomposition plugin.
230     * This will ensure the SCIP stage is SCIP_STAGE_SOLVING, allowing the use of probing mode. */
231    SCIP_CALL( SCIPinterruptSolve(scip) );
232 
233    SCIP_CALL(SCIPdropEvent(scip, SCIP_EVENTTYPE_NODEFOCUSED, eventhdlr, NULL, eventhdlrdata->filterpos));
234    eventhdlrdata->filterpos = -1;
235 
236    return SCIP_OKAY;
237 }
238 
239 /** solving process initialization method of event handler (called when branch and bound process is about to begin) */
240 static
SCIP_DECL_EVENTINITSOL(eventInitsolBendersNodefocus)241 SCIP_DECL_EVENTINITSOL(eventInitsolBendersNodefocus)
242 {
243    assert(scip != NULL);
244    assert(eventhdlr != NULL);
245    assert(strcmp(SCIPeventhdlrGetName(eventhdlr), NODEFOCUS_EVENTHDLR_NAME) == 0);
246 
247    SCIP_CALL( initsolEventhandler(scip, eventhdlr, SCIP_EVENTTYPE_NODEFOCUSED) );
248 
249    return SCIP_OKAY;
250 }
251 
252 /** solving process deinitialization method of event handler (called before branch and bound process data is freed) */
253 static
SCIP_DECL_EVENTEXITSOL(eventExitsolBendersNodefocus)254 SCIP_DECL_EVENTEXITSOL(eventExitsolBendersNodefocus)
255 {
256    assert(scip != NULL);
257    assert(eventhdlr != NULL);
258    assert(strcmp(SCIPeventhdlrGetName(eventhdlr), NODEFOCUS_EVENTHDLR_NAME) == 0);
259 
260    SCIP_CALL( exitsolEventhandler(scip, eventhdlr, SCIP_EVENTTYPE_NODEFOCUSED) );
261 
262    return SCIP_OKAY;
263 }
264 
265 /** deinitialization method of event handler (called before transformed problem is freed) */
266 static
SCIP_DECL_EVENTEXIT(eventExitBendersNodefocus)267 SCIP_DECL_EVENTEXIT(eventExitBendersNodefocus)
268 {
269    assert(scip != NULL);
270    assert(eventhdlr != NULL);
271    assert(strcmp(SCIPeventhdlrGetName(eventhdlr), NODEFOCUS_EVENTHDLR_NAME) == 0);
272 
273    SCIP_CALL( exitEventhandler(scip, eventhdlr) );
274 
275    return SCIP_OKAY;
276 }
277 
278 /** deinitialization method of event handler (called before transformed problem is freed) */
279 static
SCIP_DECL_EVENTFREE(eventFreeBendersNodefocus)280 SCIP_DECL_EVENTFREE(eventFreeBendersNodefocus)
281 {
282    assert(scip != NULL);
283    assert(eventhdlr != NULL);
284    assert(strcmp(SCIPeventhdlrGetName(eventhdlr), NODEFOCUS_EVENTHDLR_NAME) == 0);
285 
286    SCIP_CALL( freeEventhandler(scip, eventhdlr) );
287 
288    return SCIP_OKAY;
289 }
290 
291 
292 /* ---------------- Callback methods of MIP solve node focus event handler ---------------- */
293 
294 /** exec the event handler */
295 static
SCIP_DECL_EVENTEXEC(eventExecBendersMipnodefocus)296 SCIP_DECL_EVENTEXEC(eventExecBendersMipnodefocus)
297 {  /*lint --e{715}*/
298    SCIP_EVENTHDLRDATA* eventhdlrdata;
299 
300    assert(scip != NULL);
301    assert(eventhdlr != NULL);
302    assert(strcmp(SCIPeventhdlrGetName(eventhdlr), MIPNODEFOCUS_EVENTHDLR_NAME) == 0);
303 
304    eventhdlrdata = SCIPeventhdlrGetData(eventhdlr);
305 
306    /* interrupting the solve so that the control is returned back to the Benders' core. */
307    if( eventhdlrdata->numruns == 0 && !eventhdlrdata->solvecip )
308    {
309       SCIP_CALL( SCIPinterruptSolve(scip) );
310    }
311 
312    SCIP_CALL(SCIPdropEvent(scip, SCIP_EVENTTYPE_NODEFOCUSED, eventhdlr, NULL, eventhdlrdata->filterpos));
313    eventhdlrdata->filterpos = -1;
314 
315    eventhdlrdata->numruns++;
316 
317    return SCIP_OKAY;
318 }
319 
320 /** solving process initialization method of event handler (called when branch and bound process is about to begin) */
321 static
SCIP_DECL_EVENTINITSOL(eventInitsolBendersMipnodefocus)322 SCIP_DECL_EVENTINITSOL(eventInitsolBendersMipnodefocus)
323 {
324    assert(scip != NULL);
325    assert(eventhdlr != NULL);
326    assert(strcmp(SCIPeventhdlrGetName(eventhdlr), MIPNODEFOCUS_EVENTHDLR_NAME) == 0);
327 
328    SCIP_CALL( initsolEventhandler(scip, eventhdlr, SCIP_EVENTTYPE_NODEFOCUSED) );
329 
330    return SCIP_OKAY;
331 }
332 
333 /** solving process deinitialization method of event handler (called before branch and bound process data is freed) */
334 static
SCIP_DECL_EVENTEXITSOL(eventExitsolBendersMipnodefocus)335 SCIP_DECL_EVENTEXITSOL(eventExitsolBendersMipnodefocus)
336 {
337    assert(scip != NULL);
338    assert(eventhdlr != NULL);
339    assert(strcmp(SCIPeventhdlrGetName(eventhdlr), MIPNODEFOCUS_EVENTHDLR_NAME) == 0);
340 
341    SCIP_CALL( exitsolEventhandler(scip, eventhdlr, SCIP_EVENTTYPE_NODEFOCUSED) );
342 
343    return SCIP_OKAY;
344 }
345 
346 /** deinitialization method of event handler (called before transformed problem is freed) */
347 static
SCIP_DECL_EVENTEXIT(eventExitBendersMipnodefocus)348 SCIP_DECL_EVENTEXIT(eventExitBendersMipnodefocus)
349 {
350    assert(scip != NULL);
351    assert(eventhdlr != NULL);
352    assert(strcmp(SCIPeventhdlrGetName(eventhdlr), MIPNODEFOCUS_EVENTHDLR_NAME) == 0);
353 
354    SCIP_CALL( exitEventhandler(scip, eventhdlr) );
355 
356    return SCIP_OKAY;
357 }
358 
359 /** deinitialization method of event handler (called before transformed problem is freed) */
360 static
SCIP_DECL_EVENTFREE(eventFreeBendersMipnodefocus)361 SCIP_DECL_EVENTFREE(eventFreeBendersMipnodefocus)
362 {
363    assert(scip != NULL);
364    assert(eventhdlr != NULL);
365    assert(strcmp(SCIPeventhdlrGetName(eventhdlr), MIPNODEFOCUS_EVENTHDLR_NAME) == 0);
366 
367    SCIP_CALL( freeEventhandler(scip, eventhdlr) );
368 
369    return SCIP_OKAY;
370 }
371 
372 /* ---------------- Callback methods of solution found event handler ---------------- */
373 
374 /** exec the event handler */
375 static
SCIP_DECL_EVENTEXEC(eventExecBendersUpperbound)376 SCIP_DECL_EVENTEXEC(eventExecBendersUpperbound)
377 {  /*lint --e{715}*/
378    SCIP_EVENTHDLRDATA* eventhdlrdata;
379    SCIP_SOL* bestsol;
380 
381    assert(scip != NULL);
382    assert(eventhdlr != NULL);
383    assert(strcmp(SCIPeventhdlrGetName(eventhdlr), UPPERBOUND_EVENTHDLR_NAME) == 0);
384 
385    eventhdlrdata = SCIPeventhdlrGetData(eventhdlr);
386    assert(eventhdlrdata != NULL);
387 
388    bestsol = SCIPgetBestSol(scip);
389 
390    if( SCIPisLT(scip, SCIPgetSolOrigObj(scip, bestsol)*(int)SCIPgetObjsense(scip), eventhdlrdata->upperbound) )
391    {
392       SCIP_CALL( SCIPinterruptSolve(scip) );
393    }
394 
395    return SCIP_OKAY;
396 }
397 
398 /** solving process initialization method of event handler (called when branch and bound process is about to begin) */
399 static
SCIP_DECL_EVENTINITSOL(eventInitsolBendersUpperbound)400 SCIP_DECL_EVENTINITSOL(eventInitsolBendersUpperbound)
401 {
402    assert(scip != NULL);
403    assert(eventhdlr != NULL);
404    assert(strcmp(SCIPeventhdlrGetName(eventhdlr), UPPERBOUND_EVENTHDLR_NAME) == 0);
405 
406    SCIP_CALL( initsolEventhandler(scip, eventhdlr, SCIP_EVENTTYPE_BESTSOLFOUND) );
407 
408    return SCIP_OKAY;
409 }
410 
411 /** solving process deinitialization method of event handler (called before branch and bound process data is freed) */
412 static
SCIP_DECL_EVENTEXITSOL(eventExitsolBendersUpperbound)413 SCIP_DECL_EVENTEXITSOL(eventExitsolBendersUpperbound)
414 {
415    assert(scip != NULL);
416    assert(eventhdlr != NULL);
417    assert(strcmp(SCIPeventhdlrGetName(eventhdlr), UPPERBOUND_EVENTHDLR_NAME) == 0);
418 
419    SCIP_CALL( exitsolEventhandler(scip, eventhdlr, SCIP_EVENTTYPE_BESTSOLFOUND) );
420 
421    return SCIP_OKAY;
422 }
423 
424 /** deinitialization method of event handler (called before transformed problem is freed) */
425 static
SCIP_DECL_EVENTEXIT(eventExitBendersUpperbound)426 SCIP_DECL_EVENTEXIT(eventExitBendersUpperbound)
427 {
428    assert(scip != NULL);
429    assert(eventhdlr != NULL);
430    assert(strcmp(SCIPeventhdlrGetName(eventhdlr), UPPERBOUND_EVENTHDLR_NAME) == 0);
431 
432    SCIP_CALL( exitEventhandler(scip, eventhdlr) );
433 
434    return SCIP_OKAY;
435 }
436 
437 /** deinitialization method of event handler (called before transformed problem is freed) */
438 static
SCIP_DECL_EVENTFREE(eventFreeBendersUpperbound)439 SCIP_DECL_EVENTFREE(eventFreeBendersUpperbound)
440 {
441    assert(scip != NULL);
442    assert(eventhdlr != NULL);
443    assert(strcmp(SCIPeventhdlrGetName(eventhdlr), UPPERBOUND_EVENTHDLR_NAME) == 0);
444 
445    SCIP_CALL( freeEventhandler(scip, eventhdlr) );
446 
447    return SCIP_OKAY;
448 }
449 
450 /** updates the upper bound in the event handler data */
451 static
updateEventhdlrUpperbound(SCIP_BENDERS * benders,int probnumber,SCIP_Real upperbound)452 SCIP_RETCODE updateEventhdlrUpperbound(
453    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
454    int                   probnumber,         /**< the subproblem number */
455    SCIP_Real             upperbound          /**< the upper bound value */
456    )
457 {
458    SCIP_EVENTHDLR* eventhdlr;
459    SCIP_EVENTHDLRDATA* eventhdlrdata;
460 
461    assert(benders != NULL);
462    assert(probnumber >= 0 && probnumber < benders->nsubproblems);
463 
464    eventhdlr = SCIPfindEventhdlr(SCIPbendersSubproblem(benders, probnumber), UPPERBOUND_EVENTHDLR_NAME);
465    assert(eventhdlr != NULL);
466 
467    eventhdlrdata = SCIPeventhdlrGetData(eventhdlr);
468    assert(eventhdlrdata != NULL);
469 
470    eventhdlrdata->upperbound = upperbound;
471 
472    return SCIP_OKAY;
473 }
474 
475 /* ---------------- Callback methods of the node solved event handler ---------------- */
476 
477 /** Updates the cut constant of the Benders' cuts data.
478  *  This function solves the master problem with only the auxiliary variables in the objective function.
479  */
480 static
updateSubproblemLowerbound(SCIP * masterprob,SCIP_BENDERS * benders)481 SCIP_RETCODE updateSubproblemLowerbound(
482    SCIP*                 masterprob,         /**< the SCIP instance of the master problem */
483    SCIP_BENDERS*         benders             /**< Benders' decomposition */
484    )
485 {
486    SCIP_VAR** vars;
487    int nvars;
488    int nsubproblems;
489    int i;
490    SCIP_Bool lperror;
491    SCIP_Bool cutoff;
492 
493    assert(masterprob != NULL);
494    assert(benders != NULL);
495 
496    /* don't run in probing or in repropagation */
497    if( SCIPinProbing(masterprob) || SCIPinRepropagation(masterprob) || SCIPinDive(masterprob) )
498       return SCIP_OKAY;
499 
500    nsubproblems = SCIPbendersGetNSubproblems(benders);
501 
502    SCIP_CALL( SCIPstartProbing(masterprob) );
503 
504    /* change the master problem variables to 0 */
505    nvars = SCIPgetNVars(masterprob);
506    vars = SCIPgetVars(masterprob);
507 
508    /* setting the objective function coefficient to 0 for all variables */
509    for( i = 0; i < nvars; i++ )
510    {
511       if( SCIPvarGetStatus(vars[i]) == SCIP_VARSTATUS_COLUMN )
512       {
513          SCIP_CALL( SCIPchgVarObjProbing(masterprob, vars[i], 0.0) );
514       }
515    }
516 
517    /* solving an LP for all subproblems to find the lower bound */
518    for( i = 0; i < nsubproblems; i++)
519    {
520       SCIP_VAR* auxiliaryvar;
521 
522       auxiliaryvar = SCIPbendersGetAuxiliaryVar(benders, i);
523 
524       if( SCIPvarGetStatus(auxiliaryvar) != SCIP_VARSTATUS_COLUMN )
525          continue;
526 
527       SCIP_CALL( SCIPchgVarObjProbing(masterprob, auxiliaryvar, 1.0) );
528 
529       /* solving the probing LP to get a lower bound on the auxiliary variables */
530       SCIP_CALL( SCIPsolveProbingLP(masterprob, -1, &lperror, &cutoff) );
531 
532       if( !SCIPisInfinity(masterprob, -SCIPgetSolTransObj(masterprob, NULL)) )
533          SCIPbendersUpdateSubproblemLowerbound(benders, i, SCIPgetSolTransObj(masterprob, NULL));
534 
535       SCIPdebugMsg(masterprob, "Cut constant for subproblem %d: %g\n", i,
536          SCIPbendersGetSubproblemLowerbound(benders, i));
537 
538       SCIP_CALL( SCIPchgVarObjProbing(masterprob, auxiliaryvar, 0.0) );
539    }
540 
541    SCIP_CALL( SCIPendProbing(masterprob) );
542 
543    return SCIP_OKAY;
544 }
545 
546 /** exec the event handler */
547 static
SCIP_DECL_EVENTEXEC(eventExecBendersNodesolved)548 SCIP_DECL_EVENTEXEC(eventExecBendersNodesolved)
549 {  /*lint --e{715}*/
550    SCIP_BENDERS* benders;
551 
552    assert(scip != NULL);
553    assert(eventhdlr != NULL);
554    assert(strcmp(SCIPeventhdlrGetName(eventhdlr), NODESOLVED_EVENTHDLR_NAME) == 0);
555 
556    benders = (SCIP_BENDERS*)SCIPeventhdlrGetData(eventhdlr);   /*lint !e826*/
557 
558    if( SCIPbendersGetNSubproblems(benders) > 0
559       && SCIPbendersGetNSubproblems(benders) > SCIPbendersGetNConvexSubproblems(benders) )
560    {
561       SCIP_CALL( updateSubproblemLowerbound(scip, benders) );
562    }
563 
564    SCIP_CALL( SCIPdropEvent(scip, SCIP_EVENTTYPE_NODESOLVED, eventhdlr, NULL, -1) );
565 
566    return SCIP_OKAY;
567 }
568 
569 /** solving process initialization method of event handler (called when branch and bound process is about to begin) */
570 static
SCIP_DECL_EVENTINITSOL(eventInitsolBendersNodesolved)571 SCIP_DECL_EVENTINITSOL(eventInitsolBendersNodesolved)
572 {
573    SCIP_BENDERS* benders;
574 
575    assert(scip != NULL);
576    assert(eventhdlr != NULL);
577    assert(strcmp(SCIPeventhdlrGetName(eventhdlr), NODESOLVED_EVENTHDLR_NAME) == 0);
578 
579    /* getting the Benders' decomposition data structure */
580    benders = (SCIP_BENDERS*)SCIPeventhdlrGetData(eventhdlr);   /*lint !e826*/
581 
582    /* The event is only caught if there is an active Benders' decomposition, the integer subproblem are solved and
583     * the Benders' decomposition has not been copied in thread safe mode
584     */
585    if( SCIPbendersIsActive(benders) && !SCIPbendersOnlyCheckConvexRelax(benders, SCIPgetSubscipsOff(scip))
586       && !benders->threadsafe )
587    {
588       SCIP_CALL( SCIPcatchEvent(scip, SCIP_EVENTTYPE_NODESOLVED, eventhdlr, NULL, NULL) );
589    }
590 
591    return SCIP_OKAY;
592 }
593 
594 
595 /* ---------------- Methods for the parallelisation of Benders' decomposition ---------------- */
596 
597 /** comparison method for sorting the subproblems.
598  *  The subproblem that has been called the least is prioritised
599  */
600 static
SCIP_DECL_SORTPTRCOMP(benderssubcompdefault)601 SCIP_DECL_SORTPTRCOMP(benderssubcompdefault)
602 {
603    SCIP_SUBPROBLEMSOLVESTAT* solvestat1;
604    SCIP_SUBPROBLEMSOLVESTAT* solvestat2;
605 
606    assert(elem1 != NULL);
607    assert(elem2 != NULL);
608 
609    solvestat1 = (SCIP_SUBPROBLEMSOLVESTAT*)elem1;
610    solvestat2 = (SCIP_SUBPROBLEMSOLVESTAT*)elem2;
611 
612    /* prefer subproblems with fewer calls, using the index as tie breaker */
613    if( MAX(solvestat1->ncalls, solvestat2->ncalls) == 0 )
614       return solvestat1->idx - solvestat2->idx;
615    else if( solvestat1->ncalls != solvestat2->ncalls )
616       return solvestat1->ncalls - solvestat2->ncalls;
617    else
618    {
619       /* prefer the harder problem (with more average iterations) */
620       int avgiterdiff = (int)solvestat2->avgiter - (int)solvestat1->avgiter;
621 
622       if( avgiterdiff != 0 )
623          return avgiterdiff;
624 
625       return solvestat1->idx - solvestat2->idx;
626    }
627 
628 /* the code below does not give a total order of the elements */
629 #ifdef SCIP_DISABLED_CODE
630    if( solvestat1->ncalls == 0 )
631       if( solvestat2->ncalls == 0 )
632          if( solvestat1->idx < solvestat2->idx )
633             return -1;
634          else
635             return 1;
636       else
637          return -1;
638    else if( solvestat2->ncalls == 0 )
639       return 1;
640    else
641    {
642       if( solvestat1->ncalls < solvestat2->ncalls )
643          return -1;
644       else if( solvestat2->ncalls < solvestat1->ncalls )
645          return 1;
646       else
647       {
648          /* we want to execute the hard subproblems first */
649          if( solvestat1->avgiter > solvestat2->avgiter )
650             return 1;
651          else
652             return -1;
653       }
654    }
655 #endif
656 }
657 
658 /* Local methods */
659 
660 /** A workaround for GCG. This is a temp vardata that is set for the auxiliary variables */
661 struct SCIP_VarData
662 {
663    int                   vartype;             /**< the variable type. In GCG this indicates whether the variable is a
664                                                *   master problem or subproblem variable. */
665 };
666 
667 /** adds the auxiliary variables to the Benders' decomposition master problem */
668 static
addAuxiliaryVariablesToMaster(SCIP * scip,SCIP_BENDERS * benders)669 SCIP_RETCODE addAuxiliaryVariablesToMaster(
670    SCIP*                 scip,               /**< SCIP data structure */
671    SCIP_BENDERS*         benders             /**< Benders' decomposition structure */
672    )
673 {
674    SCIP_BENDERS* topbenders;        /* the highest priority Benders' decomposition */
675    SCIP_VAR* auxiliaryvar;
676    SCIP_VARDATA* vardata;
677    char varname[SCIP_MAXSTRLEN];    /* the name of the auxiliary variable */
678    SCIP_Bool shareauxvars;
679    int i;
680 
681    /* this is a workaround for GCG. GCG expects that the variable has vardata when added. So a dummy vardata is created */
682    SCIP_CALL( SCIPallocBlockMemory(scip, &vardata) );
683    vardata->vartype = -1;
684 
685    /* getting the highest priority Benders' decomposition */
686    topbenders = SCIPgetBenders(scip)[0];
687 
688    /* if the current Benders is the highest priority Benders, then we need to create the auxiliary variables.
689     * Otherwise, if the shareauxvars flag is set, then the auxiliary variables from the highest priority Benders' are
690     * stored with this Benders. */
691    shareauxvars = FALSE;
692    if( topbenders != benders && SCIPbendersShareAuxVars(benders) )
693       shareauxvars = TRUE;
694 
695    for( i = 0; i < SCIPbendersGetNSubproblems(benders); i++ )
696    {
697       /* if the auxiliary variables are shared, then a pointer to the variable is retrieved from topbenders,
698        * otherwise the auxiliaryvariable is created. */
699       if( shareauxvars )
700       {
701          auxiliaryvar = SCIPbendersGetAuxiliaryVar(topbenders, i);
702 
703          SCIP_CALL( SCIPcaptureVar(scip, auxiliaryvar) );
704       }
705       else
706       {
707          SCIP_VARTYPE vartype;
708 
709          /* set the variable type of the auxiliary variables to implied integer if the objective function of the
710           * subproblem is guaranteed to be integer. This behaviour is controlled through a user parameter.
711           * NOTE: It is only possible to determine if the objective function is integral if the subproblem is defined as
712           * a SCIP instance, i.e. not NULL.
713           */
714          if( benders->auxvarsimplint && SCIPbendersSubproblem(benders, i) != NULL
715             && SCIPisObjIntegral(SCIPbendersSubproblem(benders, i)) )
716             vartype = SCIP_VARTYPE_IMPLINT;
717          else
718             vartype = SCIP_VARTYPE_CONTINUOUS;
719 
720          (void) SCIPsnprintf(varname, SCIP_MAXSTRLEN, "%s_%d_%s", AUXILIARYVAR_NAME, i, SCIPbendersGetName(benders) );
721          SCIP_CALL( SCIPcreateVarBasic(scip, &auxiliaryvar, varname, benders->subproblowerbound[i], SCIPinfinity(scip),
722                1.0, vartype) );
723 
724          SCIPvarSetData(auxiliaryvar, vardata);
725 
726          SCIP_CALL( SCIPaddVar(scip, auxiliaryvar) );
727 
728          /* adding the down lock for the Benders' decomposition constraint handler */
729          SCIP_CALL( SCIPaddVarLocksType(scip, auxiliaryvar, SCIP_LOCKTYPE_MODEL, 1, 0) );
730       }
731 
732       benders->auxiliaryvars[i] = auxiliaryvar;
733    }
734 
735    SCIPfreeBlockMemory(scip, &vardata);
736 
737    return SCIP_OKAY;
738 }
739 
740 /** assigns the copied auxiliary variables in the target SCIP to the target Benders' decomposition data */
741 static
assignAuxiliaryVariables(SCIP * scip,SCIP_BENDERS * benders)742 SCIP_RETCODE assignAuxiliaryVariables(
743    SCIP*                 scip,               /**< SCIP data structure, the target scip */
744    SCIP_BENDERS*         benders             /**< Benders' decomposition */
745    )
746 {
747    SCIP_BENDERS* topbenders;        /* the highest priority Benders' decomposition */
748    SCIP_VAR* targetvar;
749    SCIP_VARDATA* vardata;
750    char varname[SCIP_MAXSTRLEN];    /* the name of the auxiliary variable */
751    SCIP_Bool shareauxvars;
752    int subscipdepth;
753    int i;
754    int j;
755 
756    assert(scip != NULL);
757    assert(benders != NULL);
758 
759    /* this is a workaround for GCG. GCG expects that the variable has vardata when added. So a dummy vardata is created */
760    SCIP_CALL( SCIPallocBlockMemory(scip, &vardata) );
761    vardata->vartype = -1;
762 
763    /* getting the highest priority Benders' decomposition */
764    topbenders = SCIPgetBenders(scip)[0];
765 
766    /* if the auxiliary variable are shared, then the variable name will have a suffix of the highest priority Benders'
767     * name. So the shareauxvars flag indicates how to search for the auxiliary variables */
768    shareauxvars = FALSE;
769    if( topbenders != benders && SCIPbendersShareAuxVars(benders) )
770       shareauxvars = TRUE;
771 
772    subscipdepth = SCIPgetSubscipDepth(scip);
773 
774    for( i = 0; i < SCIPbendersGetNSubproblems(benders); i++ )
775    {
776       char prefix[SCIP_MAXSTRLEN];
777       char tmpprefix[SCIP_MAXSTRLEN];
778       int len = 1;
779 
780       j = 0;
781       targetvar = NULL;
782 
783       /* the prefix for the variable names is required for UG, since we don't know how many copies have been made. To
784        * find the target variable, we start with an empty prefix. Then t_ is prepended until the target variable is
785        * found
786        */
787       prefix[0] = '\0';
788       while( targetvar == NULL && j <= subscipdepth )
789       {
790          if( shareauxvars )
791             (void) SCIPsnprintf(varname, SCIP_MAXSTRLEN, "%s%s_%d_%s", prefix, AUXILIARYVAR_NAME, i, SCIPbendersGetName(topbenders));
792          else
793             (void) SCIPsnprintf(varname, SCIP_MAXSTRLEN, "%s%s_%d_%s", prefix, AUXILIARYVAR_NAME, i, SCIPbendersGetName(benders));
794 
795          /* finding the variable in the copied problem that has the same name as the auxiliary variable */
796          targetvar = SCIPfindVar(scip, varname);
797 
798          (void) SCIPsnprintf(tmpprefix, len, "t_%s", prefix);
799          len += 2;
800          (void) strncpy(prefix, tmpprefix, len); /*lint !e732*/
801 
802          j++;
803       }
804 
805       if( targetvar != NULL )
806       {
807          SCIPvarSetData(targetvar, vardata);
808 
809          benders->auxiliaryvars[i] = SCIPvarGetTransVar(targetvar);
810 
811          SCIP_CALL( SCIPcaptureVar(scip, benders->auxiliaryvars[i]) );
812       }
813       else
814       {
815          SCIPABORT();
816       }
817    }
818 
819    SCIPfreeBlockMemory(scip, &vardata);
820 
821    return SCIP_OKAY;
822 }
823 
824 /** sets the subproblem objective value array to -infinity */
825 static
resetSubproblemObjectiveValue(SCIP_BENDERS * benders,SCIP_SET * set)826 void resetSubproblemObjectiveValue(
827    SCIP_BENDERS*         benders,            /**< the Benders' decomposition structure */
828    SCIP_SET*             set                 /**< global SCIP settings */
829    )
830 {
831    SCIP* subproblem;
832    SCIP_Real inf;
833    int nsubproblems;
834    int i;
835 
836    assert(benders != NULL);
837 
838    nsubproblems = SCIPbendersGetNSubproblems(benders);
839 
840    for( i = 0; i < nsubproblems; i++ )
841    {
842       subproblem = SCIPbendersSubproblem(benders, i);
843       if( subproblem != NULL )
844          inf = SCIPinfinity(subproblem);
845       else
846          inf = SCIPsetInfinity(set);
847 
848       SCIPbendersSetSubproblemObjval(benders, i, inf);
849    }
850 }
851 
852 /** compares two Benders' decompositions w. r. to their priority */
SCIP_DECL_SORTPTRCOMP(SCIPbendersComp)853 SCIP_DECL_SORTPTRCOMP(SCIPbendersComp)
854 {  /*lint --e{715}*/
855    return ((SCIP_BENDERS*)elem2)->priority - ((SCIP_BENDERS*)elem1)->priority;
856 }
857 
858 /** comparison method for sorting Benders' decompositions w.r.t. to their name */
SCIP_DECL_SORTPTRCOMP(SCIPbendersCompName)859 SCIP_DECL_SORTPTRCOMP(SCIPbendersCompName)
860 {
861    return strcmp(SCIPbendersGetName((SCIP_BENDERS*)elem1), SCIPbendersGetName((SCIP_BENDERS*)elem2));
862 }
863 
864 /** method to call, when the priority of a Benders' decomposition was changed */
865 static
SCIP_DECL_PARAMCHGD(paramChgdBendersPriority)866 SCIP_DECL_PARAMCHGD(paramChgdBendersPriority)
867 {  /*lint --e{715}*/
868    SCIP_PARAMDATA* paramdata;
869 
870    paramdata = SCIPparamGetData(param);
871    assert(paramdata != NULL);
872 
873    /* use SCIPsetBendersPriority() to mark the Benders' decompositions as unsorted */
874    SCIPsetBendersPriority(scip, (SCIP_BENDERS*)paramdata, SCIPparamGetInt(param)); /*lint !e740*/
875 
876    return SCIP_OKAY;
877 }
878 
879 /** creates a variable mapping between the master problem variables of the source scip and the sub scip */
880 static
createMasterVarMapping(SCIP_BENDERS * benders,SCIP_SET * sourceset,SCIP_HASHMAP * varmap)881 SCIP_RETCODE createMasterVarMapping(
882    SCIP_BENDERS*         benders,            /**< Benders' decomposition of the target SCIP instance */
883    SCIP_SET*             sourceset,          /**< global SCIP settings from the source SCIP */
884    SCIP_HASHMAP*         varmap              /**< a hashmap to store the mapping of source variables corresponding
885                                               *   target variables; must not be NULL */
886    )
887 {
888    SCIP_VAR** vars;
889    SCIP_VAR* targetvar;
890    int nvars;
891    int i;
892 
893    assert(benders != NULL);
894    assert(sourceset != NULL);
895    assert(benders->iscopy);
896    assert(benders->mastervarsmap == NULL);
897 
898    /* getting the master problem variable data */
899    vars = SCIPgetVars(sourceset->scip);
900    nvars = SCIPgetNVars(sourceset->scip);
901 
902    /* creating the hashmap for the mapping between the master variable of the target and source scip */
903    SCIP_CALL( SCIPhashmapCreate(&benders->mastervarsmap, SCIPblkmem(sourceset->scip), nvars) );
904 
905    for( i = 0; i < nvars; i++ )
906    {
907       /* getting the variable pointer for the target SCIP variables. The variable mapping returns the target SCIP
908        * varibale for a given source SCIP variable. */
909       targetvar = (SCIP_VAR*) SCIPhashmapGetImage(varmap, vars[i]);
910       if( targetvar != NULL )
911       {
912          SCIP_CALL( SCIPhashmapInsert(benders->mastervarsmap, targetvar, vars[i]) );
913          SCIP_CALL( SCIPcaptureVar(sourceset->scip, vars[i]) );
914       }
915    }
916 
917    return SCIP_OKAY;
918 }
919 
920 /** copies the given Benders' decomposition to a new SCIP */
SCIPbendersCopyInclude(SCIP_BENDERS * benders,SCIP_SET * sourceset,SCIP_SET * targetset,SCIP_HASHMAP * varmap,SCIP_Bool threadsafe,SCIP_Bool * valid)921 SCIP_RETCODE SCIPbendersCopyInclude(
922    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
923    SCIP_SET*             sourceset,          /**< SCIP_SET of SCIP to copy from */
924    SCIP_SET*             targetset,          /**< SCIP_SET of SCIP to copy to */
925    SCIP_HASHMAP*         varmap,             /**< a hashmap to store the mapping of source variables corresponding
926                                               *   target variables; if NULL, then the transfer of cuts is not possible */
927    SCIP_Bool             threadsafe,         /**< must the Benders' decomposition copy be thread safe */
928    SCIP_Bool*            valid               /**< was the copying process valid? */
929    )
930 {
931    SCIP_BENDERS* targetbenders;  /* the copy of the Benders' decomposition struct in the target set */
932    int i;
933 
934    assert(benders != NULL);
935    assert(targetset != NULL);
936    assert(valid != NULL);
937    assert(targetset->scip != NULL);
938 
939    (*valid) = FALSE;
940 
941    if( benders->benderscopy != NULL && targetset->benders_copybenders && SCIPbendersIsActive(benders) )
942    {
943       SCIPsetDebugMsg(targetset, "including Benders' decomposition %s in subscip %p\n", SCIPbendersGetName(benders), (void*)targetset->scip);
944       SCIP_CALL( benders->benderscopy(targetset->scip, benders, threadsafe) );
945 
946       /* copying the Benders' cuts */
947       targetbenders = SCIPsetFindBenders(targetset, SCIPbendersGetName(benders));
948 
949       /* storing the pointer to the source scip instance */
950       targetbenders->sourcescip = sourceset->scip;
951 
952       /* the flag is set to indicate that the Benders' decomposition is a copy */
953       targetbenders->iscopy = TRUE;
954 
955       /* storing whether the lnscheck should be performed */
956       targetbenders->lnscheck = benders->lnscheck;
957       targetbenders->lnsmaxdepth = benders->lnsmaxdepth;
958       targetbenders->lnsmaxcalls = benders->lnsmaxcalls;
959       targetbenders->lnsmaxcallsroot = benders->lnsmaxcallsroot;
960 
961       /* storing whether the Benders' copy required thread safety */
962       targetbenders->threadsafe = threadsafe;
963 
964       /* calling the copy method for the Benders' cuts */
965       SCIPbendersSortBenderscuts(benders);
966       for( i = 0; i < benders->nbenderscuts; i++ )
967       {
968          SCIP_CALL( SCIPbenderscutCopyInclude(targetbenders, benders->benderscuts[i], targetset) );
969       }
970 
971       /* When the Benders' decomposition is copied then a variable mapping between the master problem variables is
972        * required. This variable mapping is used to transfer the cuts generated in the target SCIP to the source SCIP.
973        * The variable map is stored in the target Benders' decomposition. This will be freed when the sub-SCIP is freed.
974        */
975       if( varmap != NULL )
976       {
977          SCIP_CALL( createMasterVarMapping(targetbenders, sourceset, varmap) );
978       }
979 
980       assert((varmap != NULL && targetbenders->mastervarsmap != NULL)
981          || (varmap == NULL && targetbenders->mastervarsmap == NULL));
982    }
983 
984    /* if the Benders' decomposition is active, then copy is not valid. */
985    (*valid) = !SCIPbendersIsActive(benders);
986 
987    return SCIP_OKAY;
988 }
989 
990 /** internal method for creating a Benders' decomposition structure */
991 static
doBendersCreate(SCIP_BENDERS ** benders,SCIP_SET * set,SCIP_MESSAGEHDLR * messagehdlr,BMS_BLKMEM * blkmem,const char * name,const char * desc,int priority,SCIP_Bool cutlp,SCIP_Bool cutpseudo,SCIP_Bool cutrelax,SCIP_Bool shareauxvars,SCIP_DECL_BENDERSCOPY ((* benderscopy)),SCIP_DECL_BENDERSFREE ((* bendersfree)),SCIP_DECL_BENDERSINIT ((* bendersinit)),SCIP_DECL_BENDERSEXIT ((* bendersexit)),SCIP_DECL_BENDERSINITPRE ((* bendersinitpre)),SCIP_DECL_BENDERSEXITPRE ((* bendersexitpre)),SCIP_DECL_BENDERSINITSOL ((* bendersinitsol)),SCIP_DECL_BENDERSEXITSOL ((* bendersexitsol)),SCIP_DECL_BENDERSGETVAR ((* bendersgetvar)),SCIP_DECL_BENDERSCREATESUB ((* benderscreatesub)),SCIP_DECL_BENDERSPRESUBSOLVE ((* benderspresubsolve)),SCIP_DECL_BENDERSSOLVESUBCONVEX ((* benderssolvesubconvex)),SCIP_DECL_BENDERSSOLVESUB ((* benderssolvesub)),SCIP_DECL_BENDERSPOSTSOLVE ((* benderspostsolve)),SCIP_DECL_BENDERSFREESUB ((* bendersfreesub)),SCIP_BENDERSDATA * bendersdata)992 SCIP_RETCODE doBendersCreate(
993    SCIP_BENDERS**        benders,            /**< pointer to Benders' decomposition data structure */
994    SCIP_SET*             set,                /**< global SCIP settings */
995    SCIP_MESSAGEHDLR*     messagehdlr,        /**< message handler */
996    BMS_BLKMEM*           blkmem,             /**< block memory for parameter settings */
997    const char*           name,               /**< name of Benders' decomposition */
998    const char*           desc,               /**< description of Benders' decomposition */
999    int                   priority,           /**< priority of the Benders' decomposition */
1000    SCIP_Bool             cutlp,              /**< should Benders' cuts be generated for LP solutions */
1001    SCIP_Bool             cutpseudo,          /**< should Benders' cuts be generated for pseudo solutions */
1002    SCIP_Bool             cutrelax,           /**< should Benders' cuts be generated for relaxation solutions */
1003    SCIP_Bool             shareauxvars,       /**< should this Benders' use the highest priority Benders aux vars */
1004    SCIP_DECL_BENDERSCOPY ((*benderscopy)),   /**< copy method of Benders' decomposition or NULL if you don't want to copy your plugin into sub-SCIPs */
1005    SCIP_DECL_BENDERSFREE ((*bendersfree)),   /**< destructor of Benders' decomposition */
1006    SCIP_DECL_BENDERSINIT ((*bendersinit)),   /**< initialize Benders' decomposition */
1007    SCIP_DECL_BENDERSEXIT ((*bendersexit)),   /**< deinitialize Benders' decomposition */
1008    SCIP_DECL_BENDERSINITPRE((*bendersinitpre)),/**< presolving initialization method for Benders' decomposition */
1009    SCIP_DECL_BENDERSEXITPRE((*bendersexitpre)),/**< presolving deinitialization method for Benders' decomposition */
1010    SCIP_DECL_BENDERSINITSOL((*bendersinitsol)),/**< solving process initialization method of Benders' decomposition */
1011    SCIP_DECL_BENDERSEXITSOL((*bendersexitsol)),/**< solving process deinitialization method of Benders' decomposition */
1012    SCIP_DECL_BENDERSGETVAR((*bendersgetvar)),/**< returns the master variable for a given subproblem variable */
1013    SCIP_DECL_BENDERSCREATESUB((*benderscreatesub)),/**< creates a Benders' decomposition subproblem */
1014    SCIP_DECL_BENDERSPRESUBSOLVE((*benderspresubsolve)),/**< called prior to the subproblem solving loop */
1015    SCIP_DECL_BENDERSSOLVESUBCONVEX((*benderssolvesubconvex)),/**< the solving method for convex Benders' decomposition subproblems */
1016    SCIP_DECL_BENDERSSOLVESUB((*benderssolvesub)),/**< the solving method for the Benders' decomposition subproblems */
1017    SCIP_DECL_BENDERSPOSTSOLVE((*benderspostsolve)),/**< called after the subproblems are solved. */
1018    SCIP_DECL_BENDERSFREESUB((*bendersfreesub)),/**< the freeing method for the Benders' decomposition subproblems */
1019    SCIP_BENDERSDATA*     bendersdata         /**< Benders' decomposition data */
1020    )
1021 {
1022    char paramname[SCIP_MAXSTRLEN];
1023    char paramdesc[SCIP_MAXSTRLEN];
1024 
1025    assert(benders != NULL);
1026    assert(name != NULL);
1027    assert(desc != NULL);
1028 
1029    /* Checking whether the benderssolvesub and the bendersfreesub are both implemented or both are not implemented */
1030    if( (benderssolvesubconvex == NULL && benderssolvesub == NULL && bendersfreesub != NULL)
1031       || ((benderssolvesubconvex != NULL || benderssolvesub != NULL) && bendersfreesub == NULL) )
1032    {
1033       SCIPerrorMessage("Benders' decomposition <%s> requires that if bendersFreesub%s is implemented, then at least "
1034          "one of bendersSolvesubconvex%s or bendersSolvesub%s are implemented.\n", name, name, name, name);
1035       return SCIP_INVALIDCALL;
1036    }
1037 
1038    SCIP_ALLOC( BMSallocMemory(benders) );
1039    BMSclearMemory(*benders);
1040    SCIP_ALLOC( BMSduplicateMemoryArray(&(*benders)->name, name, strlen(name)+1) );
1041    SCIP_ALLOC( BMSduplicateMemoryArray(&(*benders)->desc, desc, strlen(desc)+1) );
1042    (*benders)->priority = priority;
1043    (*benders)->cutlp = cutlp;
1044    (*benders)->cutpseudo = cutpseudo;
1045    (*benders)->cutrelax = cutrelax;
1046    (*benders)->shareauxvars = shareauxvars;
1047    (*benders)->benderscopy = benderscopy;
1048    (*benders)->bendersfree = bendersfree;
1049    (*benders)->bendersinit = bendersinit;
1050    (*benders)->bendersexit = bendersexit;
1051    (*benders)->bendersinitpre = bendersinitpre;
1052    (*benders)->bendersexitpre = bendersexitpre;
1053    (*benders)->bendersinitsol = bendersinitsol;
1054    (*benders)->bendersexitsol = bendersexitsol;
1055    (*benders)->bendersgetvar = bendersgetvar;
1056    (*benders)->benderscreatesub = benderscreatesub;
1057    (*benders)->benderspresubsolve = benderspresubsolve;
1058    (*benders)->benderssolvesubconvex = benderssolvesubconvex;
1059    (*benders)->benderssolvesub = benderssolvesub;
1060    (*benders)->benderspostsolve = benderspostsolve;
1061    (*benders)->bendersfreesub = bendersfreesub;
1062    (*benders)->bendersdata = bendersdata;
1063    SCIP_CALL( SCIPclockCreate(&(*benders)->setuptime, SCIP_CLOCKTYPE_DEFAULT) );
1064    SCIP_CALL( SCIPclockCreate(&(*benders)->bendersclock, SCIP_CLOCKTYPE_DEFAULT) );
1065 
1066    /* add parameters */
1067    (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "benders/%s/priority", name);
1068    (void) SCIPsnprintf(paramdesc, SCIP_MAXSTRLEN, "priority of Benders' decomposition <%s>", name);
1069    SCIP_CALL( SCIPsetAddIntParam(set, messagehdlr, blkmem, paramname, paramdesc,
1070          &(*benders)->priority, FALSE, priority, INT_MIN/4, INT_MAX/4,
1071          paramChgdBendersPriority, (SCIP_PARAMDATA*)(*benders)) ); /*lint !e740*/
1072 
1073    (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "benders/%s/cutlp", name);
1074    SCIP_CALL( SCIPsetAddBoolParam(set, messagehdlr, blkmem, paramname,
1075          "should Benders' cuts be generated for LP solutions?", &(*benders)->cutlp, FALSE, cutlp, NULL, NULL) ); /*lint !e740*/
1076 
1077    (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "benders/%s/cutpseudo", name);
1078    SCIP_CALL( SCIPsetAddBoolParam(set, messagehdlr, blkmem, paramname,
1079          "should Benders' cuts be generated for pseudo solutions?", &(*benders)->cutpseudo, FALSE, cutpseudo, NULL, NULL) ); /*lint !e740*/
1080 
1081    (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "benders/%s/cutrelax", name);
1082    SCIP_CALL( SCIPsetAddBoolParam(set, messagehdlr, blkmem, paramname,
1083          "should Benders' cuts be generated for relaxation solutions?", &(*benders)->cutrelax, FALSE, cutrelax, NULL, NULL) ); /*lint !e740*/
1084 
1085    /* These parameters are left for the user to decide in a settings file. This departs from the usual SCIP convention
1086     * where the settings available at the creation of the plugin can be set in the function call.
1087     */
1088    (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "benders/%s/transfercuts", name);
1089    SCIP_CALL( SCIPsetAddBoolParam(set, messagehdlr, blkmem, paramname,
1090          "should Benders' cuts from LNS heuristics be transferred to the main SCIP instance?", &(*benders)->transfercuts,
1091          FALSE, SCIP_DEFAULT_TRANSFERCUTS, NULL, NULL) ); /*lint !e740*/
1092 
1093    (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "benders/%s/lnscheck", name);
1094    SCIP_CALL( SCIPsetAddBoolParam(set, messagehdlr, blkmem, paramname,
1095          "should Benders' decomposition be used in LNS heurisics?", &(*benders)->lnscheck, FALSE, SCIP_DEFAULT_LNSCHECK,
1096          NULL, NULL) ); /*lint !e740*/
1097 
1098    (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "benders/%s/lnsmaxdepth", name);
1099    SCIP_CALL( SCIPsetAddIntParam(set, messagehdlr, blkmem, paramname,
1100          "maximum depth at which the LNS check is performed (-1: no limit)", &(*benders)->lnsmaxdepth, TRUE,
1101          SCIP_DEFAULT_LNSMAXDEPTH, -1, SCIP_MAXTREEDEPTH, NULL, NULL) );
1102 
1103    (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "benders/%s/lnsmaxcalls", name);
1104    SCIP_CALL( SCIPsetAddIntParam(set, messagehdlr, blkmem, paramname,
1105          "the maximum number of Benders' decomposition calls in LNS heuristics (-1: no limit)", &(*benders)->lnsmaxcalls,
1106          TRUE, SCIP_DEFAULT_LNSMAXCALLS, -1, INT_MAX, NULL, NULL) );
1107 
1108    (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "benders/%s/lnsmaxcallsroot", name);
1109    SCIP_CALL( SCIPsetAddIntParam(set, messagehdlr, blkmem, paramname,
1110          "the maximum number of root node Benders' decomposition calls in LNS heuristics (-1: no limit)",
1111          &(*benders)->lnsmaxcallsroot, TRUE, SCIP_DEFAULT_LNSMAXCALLSROOT, -1, INT_MAX, NULL, NULL) );
1112 
1113    (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "benders/%s/cutsasconss", name);
1114    SCIP_CALL( SCIPsetAddBoolParam(set, messagehdlr, blkmem, paramname,
1115          "should the transferred cuts be added as constraints?", &(*benders)->cutsasconss, FALSE,
1116          SCIP_DEFAULT_CUTSASCONSS, NULL, NULL) ); /*lint !e740*/
1117 
1118    (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "benders/%s/subprobfrac", name);
1119    SCIP_CALL( SCIPsetAddRealParam(set, messagehdlr, blkmem, paramname,
1120          "fraction of subproblems that are solved in each iteration", &(*benders)->subprobfrac, FALSE,
1121          SCIP_DEFAULT_SUBPROBFRAC, 0.0, 1.0, NULL, NULL) ); /*lint !e740*/
1122 
1123    (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "benders/%s/updateauxvarbound", name);
1124    SCIP_CALL( SCIPsetAddBoolParam(set, messagehdlr, blkmem, paramname,
1125          "should the auxiliary variable bound be updated by solving the subproblem?", &(*benders)->updateauxvarbound,
1126          FALSE, SCIP_DEFAULT_UPDATEAUXVARBOUND, NULL, NULL) ); /*lint !e740*/
1127 
1128    (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "benders/%s/auxvarsimplint", name);
1129    SCIP_CALL( SCIPsetAddBoolParam(set, messagehdlr, blkmem, paramname,
1130          "if the subproblem objective is integer, then define the auxiliary variables as implied integers?",
1131          &(*benders)->auxvarsimplint, FALSE, SCIP_DEFAULT_AUXVARSIMPLINT, NULL, NULL) ); /*lint !e740*/
1132 
1133    (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "benders/%s/cutcheck", name);
1134    SCIP_CALL( SCIPsetAddBoolParam(set, messagehdlr, blkmem, paramname,
1135          "should Benders' cuts be generated while checking solutions?",
1136          &(*benders)->cutcheck, FALSE, SCIP_DEFAULT_CUTCHECK, NULL, NULL) ); /*lint !e740*/
1137 
1138    (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "benders/%s/cutstrengthenmult", name);
1139    SCIP_CALL( SCIPsetAddRealParam(set, messagehdlr, blkmem, paramname,
1140          "the convex combination multiplier for the cut strengthening", &(*benders)->convexmult, FALSE,
1141          SCIP_DEFAULT_STRENGTHENMULT, 0.0, 1.0, NULL, NULL) ); /*lint !e740*/
1142 
1143    (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "benders/%s/noimprovelimit", name);
1144    SCIP_CALL( SCIPsetAddIntParam(set, messagehdlr, blkmem, paramname,
1145          "the maximum number of cut strengthening without improvement", &(*benders)->noimprovelimit, TRUE,
1146          SCIP_DEFAULT_NOIMPROVELIMIT, 0, INT_MAX, NULL, NULL) );
1147 
1148    (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "benders/%s/corepointperturb", name);
1149    SCIP_CALL( SCIPsetAddRealParam(set, messagehdlr, blkmem, paramname,
1150          "the constant use to perturb the cut strengthening core point", &(*benders)->perturbeps, FALSE,
1151          SCIP_DEFAULT_STRENGTHENPERTURB, 0.0, 1.0, NULL, NULL) ); /*lint !e740*/
1152 
1153    (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "benders/%s/cutstrengthenenabled", name);
1154    SCIP_CALL( SCIPsetAddBoolParam(set, messagehdlr, blkmem, paramname,
1155          "should the core point cut strengthening be employed (only applied to fractional solutions or continuous subproblems)?",
1156          &(*benders)->strengthenenabled, FALSE, SCIP_DEFAULT_STRENGTHENENABLED, NULL, NULL) ); /*lint !e740*/
1157 
1158    (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "benders/%s/cutstrengthenintpoint", name);
1159    SCIP_CALL( SCIPsetAddCharParam(set, messagehdlr, blkmem, paramname,
1160          "where should the strengthening interior point be sourced from ('l'p relaxation, 'f'irst solution, 'i'ncumbent solution, 'r'elative interior point, vector of 'o'nes, vector of 'z'eros)",
1161          &(*benders)->strengthenintpoint, FALSE, SCIP_DEFAULT_STRENGTHENINTPOINT, "lfiroz", NULL, NULL) ); /*lint !e740*/
1162 
1163    (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "benders/%s/numthreads", name);
1164    SCIP_CALL( SCIPsetAddIntParam(set, messagehdlr, blkmem, paramname,
1165          "the number of threads to use when solving the subproblems", &(*benders)->numthreads, TRUE,
1166          SCIP_DEFAULT_NUMTHREADS, 1, INT_MAX, NULL, NULL) );
1167 
1168    (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "benders/%s/execfeasphase", name);
1169    SCIP_CALL( SCIPsetAddBoolParam(set, messagehdlr, blkmem, paramname,
1170          "should a feasibility phase be executed during the root node, i.e. adding slack variables to constraints to ensure feasibility",
1171          &(*benders)->execfeasphase, FALSE, SCIP_DEFAULT_EXECFEASPHASE, NULL, NULL) ); /*lint !e740*/
1172 
1173    (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "benders/%s/slackvarcoef", name);
1174    SCIP_CALL( SCIPsetAddRealParam(set, messagehdlr, blkmem, paramname,
1175          "the objective coefficient of the slack variables in the subproblem", &(*benders)->slackvarcoef, FALSE,
1176          SCIP_DEFAULT_SLACKVARCOEF, 0.0, SCIPsetInfinity(set), NULL, NULL) ); /*lint !e740*/
1177 
1178    (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "benders/%s/checkconsconvexity", name);
1179    SCIP_CALL( SCIPsetAddBoolParam(set, messagehdlr, blkmem, paramname,
1180          "should the constraints of the subproblems be checked for convexity?", &(*benders)->checkconsconvexity, FALSE,
1181          SCIP_DEFAULT_CHECKCONSCONVEXITY, NULL, NULL) ); /*lint !e740*/
1182 
1183    return SCIP_OKAY;
1184 }
1185 
1186 /** creates a Benders' decomposition structure
1187  *
1188  *  To use the Benders' decomposition for solving a problem, it first has to be activated with a call to SCIPactivateBenders().
1189  */
SCIPbendersCreate(SCIP_BENDERS ** benders,SCIP_SET * set,SCIP_MESSAGEHDLR * messagehdlr,BMS_BLKMEM * blkmem,const char * name,const char * desc,int priority,SCIP_Bool cutlp,SCIP_Bool cutpseudo,SCIP_Bool cutrelax,SCIP_Bool shareauxvars,SCIP_DECL_BENDERSCOPY ((* benderscopy)),SCIP_DECL_BENDERSFREE ((* bendersfree)),SCIP_DECL_BENDERSINIT ((* bendersinit)),SCIP_DECL_BENDERSEXIT ((* bendersexit)),SCIP_DECL_BENDERSINITPRE ((* bendersinitpre)),SCIP_DECL_BENDERSEXITPRE ((* bendersexitpre)),SCIP_DECL_BENDERSINITSOL ((* bendersinitsol)),SCIP_DECL_BENDERSEXITSOL ((* bendersexitsol)),SCIP_DECL_BENDERSGETVAR ((* bendersgetvar)),SCIP_DECL_BENDERSCREATESUB ((* benderscreatesub)),SCIP_DECL_BENDERSPRESUBSOLVE ((* benderspresubsolve)),SCIP_DECL_BENDERSSOLVESUBCONVEX ((* benderssolvesubconvex)),SCIP_DECL_BENDERSSOLVESUB ((* benderssolvesub)),SCIP_DECL_BENDERSPOSTSOLVE ((* benderspostsolve)),SCIP_DECL_BENDERSFREESUB ((* bendersfreesub)),SCIP_BENDERSDATA * bendersdata)1190 SCIP_RETCODE SCIPbendersCreate(
1191    SCIP_BENDERS**        benders,            /**< pointer to Benders' decomposition data structure */
1192    SCIP_SET*             set,                /**< global SCIP settings */
1193    SCIP_MESSAGEHDLR*     messagehdlr,        /**< message handler */
1194    BMS_BLKMEM*           blkmem,             /**< block memory for parameter settings */
1195    const char*           name,               /**< name of Benders' decomposition */
1196    const char*           desc,               /**< description of Benders' decomposition */
1197    int                   priority,           /**< priority of the Benders' decomposition */
1198    SCIP_Bool             cutlp,              /**< should Benders' cuts be generated for LP solutions */
1199    SCIP_Bool             cutpseudo,          /**< should Benders' cuts be generated for pseudo solutions */
1200    SCIP_Bool             cutrelax,           /**< should Benders' cuts be generated for relaxation solutions */
1201    SCIP_Bool             shareauxvars,       /**< should this Benders' use the highest priority Benders aux vars */
1202    SCIP_DECL_BENDERSCOPY ((*benderscopy)),   /**< copy method of Benders' decomposition or NULL if you don't want to copy your plugin into sub-SCIPs */
1203    SCIP_DECL_BENDERSFREE ((*bendersfree)),   /**< destructor of Benders' decomposition */
1204    SCIP_DECL_BENDERSINIT ((*bendersinit)),   /**< initialize Benders' decomposition */
1205    SCIP_DECL_BENDERSEXIT ((*bendersexit)),   /**< deinitialize Benders' decomposition */
1206    SCIP_DECL_BENDERSINITPRE((*bendersinitpre)),/**< presolving initialization method for Benders' decomposition */
1207    SCIP_DECL_BENDERSEXITPRE((*bendersexitpre)),/**< presolving deinitialization method for Benders' decomposition */
1208    SCIP_DECL_BENDERSINITSOL((*bendersinitsol)),/**< solving process initialization method of Benders' decomposition */
1209    SCIP_DECL_BENDERSEXITSOL((*bendersexitsol)),/**< solving process deinitialization method of Benders' decomposition */
1210    SCIP_DECL_BENDERSGETVAR((*bendersgetvar)),/**< returns the master variable for a given subproblem variable */
1211    SCIP_DECL_BENDERSCREATESUB((*benderscreatesub)),/**< creates a Benders' decomposition subproblem */
1212    SCIP_DECL_BENDERSPRESUBSOLVE((*benderspresubsolve)),/**< called prior to the subproblem solving loop */
1213    SCIP_DECL_BENDERSSOLVESUBCONVEX((*benderssolvesubconvex)),/**< the solving method for convex Benders' decomposition subproblems */
1214    SCIP_DECL_BENDERSSOLVESUB((*benderssolvesub)),/**< the solving method for the Benders' decomposition subproblems */
1215    SCIP_DECL_BENDERSPOSTSOLVE((*benderspostsolve)),/**< called after the subproblems are solved. */
1216    SCIP_DECL_BENDERSFREESUB((*bendersfreesub)),/**< the freeing method for the Benders' decomposition subproblems */
1217    SCIP_BENDERSDATA*     bendersdata         /**< Benders' decomposition data */
1218    )
1219 {
1220    assert(benders != NULL);
1221    assert(name != NULL);
1222    assert(desc != NULL);
1223 
1224    SCIP_CALL_FINALLY( doBendersCreate(benders, set, messagehdlr, blkmem, name, desc, priority, cutlp, cutpseudo,
1225          cutrelax, shareauxvars, benderscopy, bendersfree, bendersinit, bendersexit, bendersinitpre, bendersexitpre,
1226          bendersinitsol, bendersexitsol, bendersgetvar, benderscreatesub, benderspresubsolve, benderssolvesubconvex,
1227          benderssolvesub, benderspostsolve, bendersfreesub, bendersdata), (void) SCIPbendersFree(benders, set) );
1228 
1229    return SCIP_OKAY;
1230 }
1231 
1232 
1233 /** releases the variables that have been captured in the hashmap */
1234 static
releaseVarMappingHashmapVars(SCIP * scip,SCIP_BENDERS * benders)1235 SCIP_RETCODE releaseVarMappingHashmapVars(
1236    SCIP*                 scip,               /**< the SCIP data structure */
1237    SCIP_BENDERS*         benders             /**< Benders' decomposition */
1238    )
1239 {
1240    int nentries;
1241    int i;
1242 
1243    assert(scip != NULL);
1244    assert(benders != NULL);
1245 
1246    assert(benders->mastervarsmap != NULL);
1247 
1248    nentries = SCIPhashmapGetNEntries(benders->mastervarsmap);
1249 
1250    for( i = 0; i < nentries; ++i )
1251    {
1252       SCIP_HASHMAPENTRY* entry;
1253       entry = SCIPhashmapGetEntry(benders->mastervarsmap, i);
1254 
1255       if( entry != NULL )
1256       {
1257          SCIP_VAR* var;
1258          var = (SCIP_VAR*) SCIPhashmapEntryGetImage(entry);
1259 
1260          SCIP_CALL( SCIPreleaseVar(scip, &var) );
1261       }
1262    }
1263 
1264    return SCIP_OKAY;
1265 }
1266 
1267 
1268 /** calls destructor and frees memory of Benders' decomposition */
SCIPbendersFree(SCIP_BENDERS ** benders,SCIP_SET * set)1269 SCIP_RETCODE SCIPbendersFree(
1270    SCIP_BENDERS**        benders,            /**< pointer to Benders' decomposition data structure */
1271    SCIP_SET*             set                 /**< global SCIP settings */
1272    )
1273 {
1274    int i;
1275 
1276    assert(benders != NULL);
1277    assert(*benders != NULL);
1278    assert(!(*benders)->initialized);
1279    assert(set != NULL);
1280 
1281    /* call destructor of Benders' decomposition */
1282    if( (*benders)->bendersfree != NULL )
1283    {
1284       SCIP_CALL( (*benders)->bendersfree(set->scip, *benders) );
1285    }
1286 
1287    /* if the Benders' decomposition is a copy and a varmap has been passed to SCIP_BENDERS, then the variable map
1288     * between the source and the target SCIP needs to be freed.
1289     */
1290    if( (*benders)->iscopy && (*benders)->mastervarsmap != NULL )
1291    {
1292       SCIP_CALL( releaseVarMappingHashmapVars((*benders)->sourcescip, (*benders)) );
1293       SCIPhashmapFree(&(*benders)->mastervarsmap);
1294    }
1295 
1296    /* freeing the Benders' cuts */
1297    for( i = 0; i < (*benders)->nbenderscuts; i++ )
1298    {
1299       SCIP_CALL( SCIPbenderscutFree(&((*benders)->benderscuts[i]), set) );
1300    }
1301    BMSfreeMemoryArrayNull(&(*benders)->benderscuts);
1302 
1303    SCIPclockFree(&(*benders)->bendersclock);
1304    SCIPclockFree(&(*benders)->setuptime);
1305    BMSfreeMemoryArray(&(*benders)->name);
1306    BMSfreeMemoryArray(&(*benders)->desc);
1307    BMSfreeMemory(benders);
1308 
1309    return SCIP_OKAY;
1310 }
1311 
1312 /* adds a slack variable to the given constraint */
1313 static
addSlackVars(SCIP * scip,SCIP_BENDERS * benders,SCIP_CONS * cons,SCIP_CONSHDLR ** linearconshdlrs,SCIP_CONSHDLR * conshdlr_nonlinear,SCIP_CONSHDLR * conshdlr_quadratic,SCIP_CONSHDLR * conshdlr_abspower,int nlinearconshdlrs)1314 SCIP_RETCODE addSlackVars(
1315    SCIP*                 scip,               /**< the SCIP data structure */
1316    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
1317    SCIP_CONS*            cons,               /**< constraint to which the slack variable(s) is added to */
1318    SCIP_CONSHDLR**       linearconshdlrs,    /**< an array storing the linear constraint handlers */
1319    SCIP_CONSHDLR*        conshdlr_nonlinear, /**< pointer to the non-linear constraint handler */
1320    SCIP_CONSHDLR*        conshdlr_quadratic, /**< pointer to the quadratic constraint handler */
1321    SCIP_CONSHDLR*        conshdlr_abspower,  /**< pointer to the absolute power constraint handler */
1322    int                   nlinearconshdlrs    /**< the number of linear constraint handlers */
1323    )
1324 {
1325    SCIP_CONSHDLR* conshdlr;
1326    SCIP_VAR* var;
1327    SCIP_Real rhs;
1328    SCIP_Real lhs;
1329    SCIP_Real objcoef;
1330    int i;
1331    SCIP_Bool linearcons;
1332    SCIP_Bool success;
1333    char name[SCIP_MAXSTRLEN];
1334 
1335    conshdlr = SCIPconsGetHdlr(cons);
1336 
1337    /* assume that the constraint is not linear, then we check whether it is linear */
1338    linearcons = FALSE;
1339 
1340    /* checking whether the constraint is a linear constraint. If so, we add a coefficient to the constraint */
1341    for( i = 0; i < nlinearconshdlrs; ++i )
1342    {
1343       if( conshdlr == linearconshdlrs[i] )
1344       {
1345          linearcons = TRUE;
1346          break;
1347       }
1348    }
1349 
1350    if( !linearcons
1351       && (conshdlr != conshdlr_nonlinear && conshdlr != conshdlr_quadratic && conshdlr != conshdlr_abspower) )
1352    {
1353       SCIPwarningMessage(scip, "The subproblem includes constraint <%s>. "
1354          "This is not supported and the slack variable will not be added to the constraint. Feasibility cuts may be invalid.\n",
1355          SCIPconshdlrGetName(conshdlr));
1356    }
1357 
1358    if( linearcons )
1359    {
1360       rhs = SCIPconsGetRhs(scip, cons, &success);
1361       assert(success);
1362       lhs = SCIPconsGetLhs(scip, cons, &success);
1363       assert(success);
1364    }
1365    else
1366    {
1367       rhs = SCIPconsNonlinearGetRhs(scip, cons, &success);
1368       assert(success);
1369       lhs = SCIPconsNonlinearGetLhs(scip, cons, &success);
1370       assert(success);
1371    }
1372 
1373    /* getting the objective coefficient for the slack variables */
1374    objcoef = benders->slackvarcoef;
1375 
1376    /* if the right hand side is finite, then we need to add a slack variable with a negative coefficient */
1377    if( !SCIPisInfinity(scip, rhs) )
1378    {
1379       (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_%s_neg", SLACKVAR_NAME, SCIPconsGetName(cons) );
1380 
1381       SCIP_CALL( SCIPcreateVarBasic(scip, &var, name, 0.0, SCIPinfinity(scip), objcoef, SCIP_VARTYPE_CONTINUOUS) );
1382 
1383       /* adding the slack variable to the subproblem */
1384       SCIP_CALL( SCIPaddVar(scip, var) );
1385 
1386       /* adds the slack variable to the constraint */
1387       if( linearcons )
1388       {
1389          SCIP_CALL( SCIPconsAddCoef(scip, cons, var, -1.0) );
1390       }
1391       else
1392       {
1393          SCIP_CALL( SCIPconsNonlinearAddLinearCoef(scip, cons, var, -1.0) );
1394       }
1395 
1396       /* releasing the variable */
1397       SCIP_CALL( SCIPreleaseVar(scip, &var) );
1398    }
1399 
1400    /* if the left hand side if finite, then we need to add a slack variable with a positive coefficient */
1401    if( !SCIPisInfinity(scip, -lhs) )
1402    {
1403       (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_%s_pos", SLACKVAR_NAME, SCIPconsGetName(cons) );
1404 
1405       SCIP_CALL( SCIPcreateVarBasic(scip, &var, name, 0.0, SCIPinfinity(scip), objcoef, SCIP_VARTYPE_CONTINUOUS) );
1406 
1407       /* adding the slack variable to the subproblem */
1408       SCIP_CALL( SCIPaddVar(scip, var) );
1409 
1410       /* adds the slack variable to the constraint */
1411       if( linearcons )
1412       {
1413          SCIP_CALL( SCIPconsAddCoef(scip, cons, var, 1.0) );
1414       }
1415       else
1416       {
1417          SCIP_CALL( SCIPconsNonlinearAddLinearCoef(scip, cons, var, 1.0) );
1418       }
1419 
1420       /* releasing the variable */
1421       SCIP_CALL( SCIPreleaseVar(scip, &var) );
1422    }
1423 
1424    return SCIP_OKAY;
1425 }
1426 
1427 /** adds the slack variables to each of the constraints for the generation of feasibility cuts for the given non-linear
1428  * subproblem
1429  */
1430 static
addSlackVarsToConstraints(SCIP_BENDERS * benders,SCIP_SET * set,int probnumber)1431 SCIP_RETCODE addSlackVarsToConstraints(
1432    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
1433    SCIP_SET*             set,                /**< global SCIP settings */
1434    int                   probnumber          /**< the subproblem number */
1435    )
1436 {
1437    SCIP* subproblem;
1438    SCIP_CONSHDLR* linearconshdlrs[NLINEARCONSHDLRS];
1439    SCIP_CONSHDLR* conshdlr_nonlinear;
1440    SCIP_CONSHDLR* conshdlr_quadratic;
1441    SCIP_CONSHDLR* conshdlr_abspower;
1442    SCIP_CONS* cons;
1443    int i;
1444 
1445    assert(benders != NULL);
1446    assert(set != NULL);
1447    assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders));
1448 
1449    subproblem = SCIPbendersSubproblem(benders, probnumber);
1450 
1451    /* get pointers to linear constraints handlers, so can avoid string comparisons */
1452    linearconshdlrs[0] = SCIPfindConshdlr(subproblem, "knapsack");
1453    linearconshdlrs[1] = SCIPfindConshdlr(subproblem, "linear");
1454    linearconshdlrs[2] = SCIPfindConshdlr(subproblem, "logicor");
1455    linearconshdlrs[3] = SCIPfindConshdlr(subproblem, "setppc");
1456    linearconshdlrs[4] = SCIPfindConshdlr(subproblem, "varbound");
1457 
1458    conshdlr_nonlinear = SCIPfindConshdlr(subproblem, "nonlinear");
1459    conshdlr_quadratic = SCIPfindConshdlr(subproblem, "quadratic");
1460    conshdlr_abspower = SCIPfindConshdlr(subproblem, "abspower");
1461 
1462    for( i = 0; i < SCIPgetNOrigConss(subproblem); ++i )
1463    {
1464       cons = SCIPgetOrigConss(subproblem)[i];
1465 
1466       /* adding the slack variables to the constraint */
1467       SCIP_CALL( addSlackVars(subproblem, benders, cons, linearconshdlrs, conshdlr_nonlinear, conshdlr_quadratic,
1468             conshdlr_abspower, NLINEARCONSHDLRS) );
1469    }
1470 
1471    return SCIP_OKAY;
1472 }
1473 
1474 /** initialises a MIP subproblem by putting the problem into SCIP_STAGE_SOLVING. This is achieved by calling SCIPsolve
1475  *  and then interrupting the solve in a node focus event handler.
1476  *  The LP subproblem is also initialised using this method; however, a different event handler is added. This event
1477  *  handler will put the LP subproblem into probing mode.
1478  *  The MIP solving function is called to initialise the subproblem because this function calls SCIPsolve with the
1479  *  appropriate parameter settings for Benders' decomposition.
1480  */
1481 static
initialiseSubproblem(SCIP_BENDERS * benders,SCIP_SET * set,int probnumber,SCIP_Bool * success)1482 SCIP_RETCODE initialiseSubproblem(
1483    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
1484    SCIP_SET*             set,                /**< global SCIP settings */
1485    int                   probnumber,         /**< the subproblem number */
1486    SCIP_Bool*            success             /**< was the initialisation process successful */
1487    )
1488 {
1489    SCIP* subproblem;
1490    SCIP_STATUS solvestatus;
1491    SCIP_Bool cutoff;
1492 
1493    assert(benders != NULL);
1494    assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders));
1495    assert(success != NULL);
1496 
1497    (*success) = FALSE;
1498 
1499    subproblem = SCIPbendersSubproblem(benders, probnumber);
1500    assert(subproblem != NULL);
1501 
1502    /* Getting the problem into the right SCIP stage for solving */
1503    SCIP_CALL( SCIPbendersSolveSubproblemCIP(set->scip, benders, probnumber, &solvestatus, FALSE) );
1504 
1505    /* Constructing the LP that can be solved in later iterations */
1506    if( solvestatus != SCIP_STATUS_BESTSOLLIMIT && solvestatus != SCIP_STATUS_TIMELIMIT
1507       && solvestatus != SCIP_STATUS_MEMLIMIT )
1508    {
1509       assert(SCIPgetStage(subproblem) == SCIP_STAGE_SOLVING);
1510 
1511       SCIP_CALL( SCIPconstructLP(subproblem, &cutoff) );
1512       (*success) = TRUE;
1513    }
1514 
1515    return SCIP_OKAY;
1516 }
1517 
1518 
1519 /** initialises an LP subproblem by putting the problem into probing mode. The probing mode is invoked in a node focus
1520  *  event handler. This event handler is added just prior to calling the initialise subproblem function.
1521  */
1522 static
initialiseLPSubproblem(SCIP_BENDERS * benders,SCIP_SET * set,int probnumber)1523 SCIP_RETCODE initialiseLPSubproblem(
1524    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
1525    SCIP_SET*             set,                /**< global SCIP settings */
1526    int                   probnumber          /**< the subproblem number */
1527    )
1528 {
1529    SCIP* subproblem;
1530    SCIP_EVENTHDLR* eventhdlr;
1531    SCIP_EVENTHDLRDATA* eventhdlrdata;
1532    SCIP_Bool success;
1533 
1534    assert(benders != NULL);
1535    assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders));
1536 
1537    subproblem = SCIPbendersSubproblem(benders, probnumber);
1538    assert(subproblem != NULL);
1539 
1540    /* include event handler into SCIP */
1541    SCIP_CALL( SCIPallocBlockMemory(subproblem, &eventhdlrdata) );
1542 
1543    SCIP_CALL( initEventhandlerData(subproblem, eventhdlrdata) );
1544 
1545    SCIP_CALL( SCIPincludeEventhdlrBasic(subproblem, &eventhdlr, NODEFOCUS_EVENTHDLR_NAME, NODEFOCUS_EVENTHDLR_DESC,
1546          eventExecBendersNodefocus, eventhdlrdata) );
1547    SCIP_CALL( SCIPsetEventhdlrInitsol(subproblem, eventhdlr, eventInitsolBendersNodefocus) );
1548    SCIP_CALL( SCIPsetEventhdlrExitsol(subproblem, eventhdlr, eventExitsolBendersNodefocus) );
1549    SCIP_CALL( SCIPsetEventhdlrExit(subproblem, eventhdlr, eventExitBendersNodefocus) );
1550    SCIP_CALL( SCIPsetEventhdlrFree(subproblem, eventhdlr, eventFreeBendersNodefocus) );
1551    assert(eventhdlr != NULL);
1552 
1553    /* calling an initial solve to put the problem into probing mode */
1554    SCIP_CALL( initialiseSubproblem(benders, set, probnumber, &success) );
1555 
1556    return SCIP_OKAY; /*lint !e438*/
1557 }
1558 
1559 /** checks whether the convex relaxation of the subproblem is sufficient to solve the original problem to optimality
1560  *
1561  * We check whether we can conclude that the CIP is actually an LP or a convex NLP.
1562  * To do this, we check that all variables are of continuous type and that every constraint is either handled by known
1563  * linear constraint handler (knapsack, linear, logicor, setppc, varbound) or a known nonlinear constraint handler
1564  * (nonlinear, quadratic, abspower). In the latter case, we also check whether the nonlinear constraint is convex.
1565  * Further, nonlinear constraints are only considered if an NLP solver interface is available, i.e., and NLP could
1566  * be solved.
1567  * If constraints are present that cannot be identified as linear or convex nonlinear, then we assume that the
1568  * problem is not convex, thus solving its LP or NLP relaxation will not be sufficient.
1569  */
1570 static
checkSubproblemConvexity(SCIP_BENDERS * benders,SCIP_SET * set,int probnumber)1571 SCIP_RETCODE checkSubproblemConvexity(
1572    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
1573    SCIP_SET*             set,                /**< global SCIP settings */
1574    int                   probnumber          /**< the subproblem number, or -1 for the master problem */
1575    )
1576 {
1577    SCIP* subproblem;
1578    SCIP_CONSHDLR* conshdlr;
1579    SCIP_CONS* cons;
1580    SCIP_HASHMAP* assumevarfixed;
1581    SCIP_VAR** vars;
1582    int nvars;
1583    int nbinvars;
1584    int nintvars;
1585    int nimplintvars;
1586    int i;
1587    int j;
1588    SCIP_Bool convexcons;
1589    SCIP_Bool discretevar;
1590    SCIP_Bool isnonlinear;
1591    SCIP_CONSHDLR* linearconshdlrs[NLINEARCONSHDLRS];
1592    SCIP_CONSHDLR* conshdlr_nonlinear = NULL;
1593    SCIP_CONSHDLR* conshdlr_quadratic = NULL;
1594    SCIP_CONSHDLR* conshdlr_abspower = NULL;
1595 
1596    assert(benders != NULL);
1597    assert(set != NULL);
1598    assert(probnumber >= -1 && probnumber < SCIPbendersGetNSubproblems(benders));
1599 
1600    assumevarfixed = NULL;
1601    if( probnumber >= 0 )
1602       subproblem = SCIPbendersSubproblem(benders, probnumber);
1603    else
1604       subproblem = set->scip;
1605 
1606    assert(subproblem != NULL);
1607 
1608    convexcons = FALSE;
1609    discretevar = FALSE;
1610    isnonlinear = FALSE;
1611 
1612    /* getting the number of integer and binary variables to determine the problem type */
1613    SCIP_CALL( SCIPgetVarsData(subproblem, &vars, &nvars, &nbinvars, &nintvars, &nimplintvars, NULL) );
1614 
1615    /* if there are any binary, integer or implied integer variables, then the subproblems is marked as non-convex */
1616    if( nbinvars != 0 || nintvars != 0 || nimplintvars != 0 )
1617    {
1618       discretevar = TRUE;
1619    }
1620 
1621    /* get pointers to linear constraints handlers, so can avoid string comparisons */
1622    linearconshdlrs[0] = SCIPfindConshdlr(subproblem, "knapsack");
1623    linearconshdlrs[1] = SCIPfindConshdlr(subproblem, "linear");
1624    linearconshdlrs[2] = SCIPfindConshdlr(subproblem, "logicor");
1625    linearconshdlrs[3] = SCIPfindConshdlr(subproblem, "setppc");
1626    linearconshdlrs[4] = SCIPfindConshdlr(subproblem, "varbound");
1627 
1628    /* Get pointers to interesting nonlinear constraint handlers, if we also have an NLP solver to solve NLPs.
1629     * If there is no NLP solver, but there are (convex) nonlinear constraints, then the LP relaxation of subproblems
1630     * will (currently) not be sufficient to solve subproblems to optimality. Thus, we also take the presence of convex
1631     * nonlinear constraints as signal for having to solve the CIP eventually, thus, by abuse of notation,
1632     * return not-convex here. In summary, we do not need to have a special look onto non-linear constraints
1633     * if no NLP solver is present, and can treat them as any other constraint that is not of linear type.
1634     */
1635    if( SCIPgetNNlpis(subproblem) > 0 )
1636    {
1637       conshdlr_nonlinear = SCIPfindConshdlr(subproblem, "nonlinear");
1638       conshdlr_quadratic = SCIPfindConshdlr(subproblem, "quadratic");
1639       conshdlr_abspower = SCIPfindConshdlr(subproblem, "abspower");
1640    }
1641 
1642    /* if the quadratic constraint handler exists, then we create a hashmap of variables that can be assumed to be fixed.
1643     * These variables correspond to the copies of the master variables in the subproblem
1644     */
1645    if( probnumber >= 0 && conshdlr_quadratic != NULL )
1646    {
1647       SCIP_VAR* mappedvar;
1648 
1649       SCIP_CALL( SCIPhashmapCreate(&assumevarfixed, SCIPblkmem(set->scip), SCIPgetNVars(subproblem)) );
1650 
1651       /* finding the subproblem variables that correspond to master variables */
1652       for( i = 0; i < nvars; i++ )
1653       {
1654          /* getting the corresponding master problem variable for the given variable */
1655          SCIP_CALL( SCIPbendersGetVar(benders, set, vars[i], &mappedvar, -1) );
1656 
1657          /* if the mapped variable is not NULL, then it must be stored as a possible fixed variable */
1658          if( mappedvar != NULL )
1659          {
1660             SCIP_CALL( SCIPhashmapInsert(assumevarfixed, vars[i], vars[i]) );
1661          }
1662       }
1663    }
1664 
1665    for( i = 0; i < SCIPgetNOrigConss(subproblem); ++i )
1666    {
1667       cons = SCIPgetOrigConss(subproblem)[i];
1668       conshdlr = SCIPconsGetHdlr(cons);
1669 
1670       for( j = 0; j < NLINEARCONSHDLRS; ++j )
1671          if( conshdlr == linearconshdlrs[j] )
1672             break;
1673 
1674       /* if linear constraint, then we are good */
1675       if( j < NLINEARCONSHDLRS )
1676       {
1677 #ifdef SCIP_MOREDEBUG
1678          SCIPdebugMsg(subproblem, "subproblem <%s>: constraint <%s> is linear\n", SCIPgetProbName(subproblem), SCIPconsGetName(cons));
1679 #endif
1680          continue;
1681       }
1682 
1683       /* if cons_nonlinear (and conshdlr_nonlinear != NULL), then check whether convex */
1684       if( conshdlr == conshdlr_nonlinear )
1685       {
1686          SCIP_EXPRCURV curvature;
1687 
1688          isnonlinear = TRUE;
1689 
1690          SCIP_CALL( SCIPgetCurvatureNonlinear(subproblem, cons, TRUE, &curvature) );
1691          if( ((SCIPisInfinity(subproblem, -SCIPgetLhsNonlinear(subproblem, cons)) || (curvature & SCIP_EXPRCURV_CONCAVE) == SCIP_EXPRCURV_CONCAVE)) &&
1692              ((SCIPisInfinity(subproblem,  SCIPgetRhsNonlinear(subproblem, cons)) || (curvature & SCIP_EXPRCURV_CONVEX) == SCIP_EXPRCURV_CONVEX)) )
1693          {
1694 #ifdef SCIP_MOREDEBUG
1695             SCIPdebugMsg(subproblem, "subproblem <%s>: nonlinear constraint <%s> is convex\n", SCIPgetProbName(subproblem), SCIPconsGetName(cons));
1696 #endif
1697             continue;
1698          }
1699          else
1700          {
1701 #ifdef SCIP_MOREDEBUG
1702             SCIPdebugMsg(subproblem, "subproblem <%s>: nonlinear constraint <%s> is not convex\n", SCIPgetProbName(subproblem), SCIPconsGetName(cons));
1703 #endif
1704             goto TERMINATE;
1705          }
1706       }
1707 
1708       /* if cons_quadratic (and conshdlr_quadratic != NULL), then check whether convex */
1709       if( conshdlr == conshdlr_quadratic )
1710       {
1711          SCIP_Bool isconvex;
1712          isnonlinear = TRUE;
1713 
1714          SCIP_CALL( SCIPisConvexConsQuadratic(subproblem, cons, assumevarfixed, &isconvex) );
1715 
1716          if( isconvex )
1717          {
1718 #ifdef SCIP_MOREDEBUG
1719             SCIPdebugMsg(subproblem, "subproblem <%s>: quadratic constraint <%s> is convex\n", SCIPgetProbName(subproblem), SCIPconsGetName(cons));
1720 #endif
1721             continue;
1722          }
1723          else
1724          {
1725 #ifdef SCIP_MOREDEBUG
1726             SCIPdebugMsg(subproblem, "subproblem <%s>: quadratic constraint <%s> not convex\n", SCIPgetProbName(subproblem), SCIPconsGetName(cons));
1727 #endif
1728             goto TERMINATE;
1729          }
1730       }
1731 
1732       /* if cons_abspower (and conshdlr_abspower != NULL), then check whether convex */
1733       if( conshdlr == conshdlr_abspower )
1734       {
1735          isnonlinear = TRUE;
1736 
1737          if( SCIPisConvexAbspower(subproblem, cons) )
1738          {
1739 #ifdef SCIP_MOREDEBUG
1740             SCIPdebugMsg(subproblem, "subproblem <%s>: abspower constraint <%s> is convex\n", SCIPgetProbName(subproblem), SCIPconsGetName(cons));
1741 #endif
1742             continue;
1743          }
1744          else
1745          {
1746 #ifdef SCIP_MOREDEBUG
1747             SCIPdebugMsg(subproblem, "subproblem <%s>: abspower constraint <%s> not convex\n", SCIPgetProbName(subproblem), SCIPconsGetName(cons));
1748 #endif
1749             goto TERMINATE;
1750          }
1751       }
1752 
1753       /* skip bivariate constraints: they are typically nonconvex
1754        * skip soc constraints: it would depend how these are represented in the NLP eventually, which could be nonconvex
1755        */
1756 
1757 #ifdef SCIP_MOREDEBUG
1758       SCIPdebugMsg(subproblem, "subproblem <%s>: potentially nonconvex constraint <%s>\n", SCIPgetProbName(subproblem), SCIPconsGetName(cons));
1759 #endif
1760       goto TERMINATE;
1761    }
1762 
1763    /* if we made it until here, then all constraints are known and convex */
1764    convexcons = TRUE;
1765 
1766 TERMINATE:
1767    /* setting the flag for the convexity of the subproblem. If convexity doesn't need to be checked, then it is assumed
1768     * that the subproblems are convex. However, if there are discrete variables, then the problem must be set as
1769     * non-convex. The discrete master variables will be changed to continuous, but this will happen at the first call to
1770     * SCIPbendersSetupSubproblem
1771     */
1772    if( probnumber >= 0 )
1773    {
1774       convexcons = convexcons || !benders->checkconsconvexity;
1775 
1776       if( convexcons && !discretevar )
1777          SCIPbendersSetSubproblemType(benders, probnumber, SCIP_BENDERSSUBTYPE_CONVEXCONT);
1778       else if( convexcons && discretevar )
1779          SCIPbendersSetSubproblemType(benders, probnumber, SCIP_BENDERSSUBTYPE_CONVEXDIS);
1780       else if( !convexcons && !discretevar )
1781          SCIPbendersSetSubproblemType(benders, probnumber, SCIP_BENDERSSUBTYPE_NONCONVEXCONT);
1782       else if( !convexcons && discretevar )
1783          SCIPbendersSetSubproblemType(benders, probnumber, SCIP_BENDERSSUBTYPE_NONCONVEXDIS);
1784       else
1785          SCIPABORT();
1786    }
1787 
1788    /* setting the non-linear subproblem flag */
1789    if( probnumber >= 0 )
1790       SCIPbendersSetSubproblemIsNonlinear(benders, probnumber, isnonlinear);
1791    else
1792       SCIPbendersSetMasterIsNonlinear(benders, isnonlinear);
1793 
1794    if( probnumber >= 0 )
1795    {
1796       SCIPdebugMsg(subproblem, "subproblem <%s> has been found to be of type %d\n", SCIPgetProbName(subproblem),
1797          SCIPbendersGetSubproblemType(benders, probnumber));
1798    }
1799 
1800    /* releasing the fixed variable hashmap */
1801    if( assumevarfixed != NULL )
1802       SCIPhashmapFree(&assumevarfixed);
1803 
1804    return SCIP_OKAY;
1805 }
1806 
1807 /** creates the subproblems and registers it with the Benders' decomposition struct */
1808 static
createSubproblems(SCIP_BENDERS * benders,SCIP_SET * set)1809 SCIP_RETCODE createSubproblems(
1810    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
1811    SCIP_SET*             set                 /**< global SCIP settings */
1812    )
1813 {
1814    SCIP* subproblem;
1815    SCIP_EVENTHDLR* eventhdlr;
1816    SCIP_VAR* mastervar;
1817    SCIP_VAR** vars;
1818    int nvars;
1819    int nsubproblems;
1820    int i;
1821    int j;
1822 
1823    assert(benders != NULL);
1824    assert(set != NULL);
1825 
1826    /* if the subproblems have already been created, then they will not be created again. This is the case if the
1827     * transformed problem has been freed and then retransformed. The subproblems should only be created when the problem
1828     * is first transformed. */
1829    if( benders->subprobscreated )
1830       return SCIP_OKAY;
1831 
1832    nsubproblems = SCIPbendersGetNSubproblems(benders);
1833 
1834    /* creating all subproblems */
1835    for( i = 0; i < nsubproblems; i++ )
1836    {
1837       /* calling the create subproblem call back method */
1838       SCIP_CALL( benders->benderscreatesub(set->scip, benders, i) );
1839 
1840       subproblem = SCIPbendersSubproblem(benders, i);
1841 
1842       /* the subproblem SCIP instance could be set to NULL. This is because user defined subproblem solving methods
1843        * could be used that don't solve a SCIP instance. Thus, the following setup of the subproblem SCIP instance is
1844        * not required.
1845        *
1846        * NOTE: since the subproblems are supplied as NULL pointers, the internal convexity check can not be performed.
1847        * The user needs to specify whether the subproblems are convex or not.
1848        */
1849       if( subproblem != NULL )
1850       {
1851          /* setting global limits for the subproblems. This overwrites the limits set by the user */
1852          SCIP_CALL( SCIPsetIntParam(subproblem, "limits/maxorigsol", 0) );
1853 
1854          /* getting the number of integer and binary variables to determine the problem type */
1855          SCIP_CALL( SCIPgetVarsData(subproblem, &vars, &nvars, NULL, NULL, NULL, NULL) );
1856 
1857          /* The objective function coefficients of the master problem are set to zero. This is necessary for the Benders'
1858           * decomposition algorithm, since the cut methods and the objective function check assumes that the objective
1859           * coefficients of the master problem variables are zero.
1860           *
1861           * This only occurs if the Benders' decomposition is not a copy. It is assumed that the correct objective
1862           * coefficients are given during the first subproblem creation.
1863           *
1864           * If the subproblems were copied, then the master variables will be checked to ensure that they have a zero
1865           * objective value.
1866           */
1867          if( !benders->iscopy || benders->threadsafe )
1868          {
1869             SCIP_Bool objchanged = FALSE;
1870 
1871             assert(SCIPgetStage(subproblem) == SCIP_STAGE_PROBLEM);
1872             for( j = 0; j < nvars; j++ )
1873             {
1874                /* retrieving the master problem variable */
1875                SCIP_CALL( SCIPbendersGetVar(benders, set, vars[j], &mastervar, -1) );
1876 
1877                /* if mastervar is not NULL, then the subproblem variable has a corresponding master problem variable */
1878                if( mastervar != NULL && !SCIPisZero(subproblem, SCIPvarGetObj(vars[j])) )
1879                {
1880                   SCIPverbMessage(subproblem, SCIP_VERBLEVEL_FULL, NULL, "Benders' decomposition: Changing the objective "
1881                      "coefficient of copy of master problem variable <%s> in subproblem %d to zero.\n",
1882                      SCIPvarGetName(mastervar), i);
1883                   /* changing the subproblem variable objective coefficient to zero */
1884                   SCIP_CALL( SCIPchgVarObj(subproblem, vars[j], 0.0) );
1885 
1886                   objchanged = TRUE;
1887                }
1888             }
1889 
1890             if( objchanged )
1891             {
1892                SCIPverbMessage(subproblem, SCIP_VERBLEVEL_HIGH, NULL, "Benders' decomposition: Objective coefficients of "
1893                   "copied of master problem variables has been changed to zero.\n");
1894             }
1895          }
1896 
1897          /* checking the convexity of the subproblem. The convexity of the subproblem indicates whether the convex
1898           * relaxation is a valid relaxation for the problem
1899           */
1900          SCIP_CALL( checkSubproblemConvexity(benders, set, i) );
1901 
1902          /* if the problem is convex and has nonlinear constraints, then slack variables must be added to each of the
1903           * constraints
1904              */
1905          if( benders->execfeasphase ||
1906             (SCIPbendersGetSubproblemType(benders, i) <= SCIP_BENDERSSUBTYPE_CONVEXDIS
1907              && SCIPbendersSubproblemIsNonlinear(benders, i)) )
1908          {
1909             /* the slack variables are only added to the subproblem once. If the initialisation methods are called from a
1910              * copy, then the slack variables are not re-added. Alternatively, if the copy must be threadsafe, then the
1911              * subproblems are created from scratch again, so the slack variables need to be added.
1912              */
1913             if( !benders->iscopy || benders->threadsafe )
1914             {
1915                SCIP_CALL( addSlackVarsToConstraints(benders, set, i) );
1916             }
1917 
1918             /* setting the flag to indicate that slack variables have been added to the subproblem constraints. This is only
1919              * set if the slack variables have been added at the request of the user.
1920              */
1921             if( benders->execfeasphase )
1922                benders->feasibilityphase = TRUE;
1923          }
1924 
1925          /* after checking the subproblem for convexity, if the subproblem has convex constraints and continuous variables,
1926           * then the problem is entered into probing mode. Otherwise, it is initialised as a CIP
1927           */
1928          if( SCIPbendersGetSubproblemType(benders, i) == SCIP_BENDERSSUBTYPE_CONVEXCONT )
1929          {
1930             /* if the user has not implemented a solve subproblem callback, then the subproblem solves are performed
1931              * internally. To be more efficient the subproblem is put into probing mode. */
1932             if( benders->benderssolvesubconvex == NULL && benders->benderssolvesub == NULL
1933                && SCIPgetStage(subproblem) <= SCIP_STAGE_PROBLEM )
1934             {
1935                SCIP_CALL( initialiseLPSubproblem(benders, set, i) );
1936             }
1937          }
1938          else
1939          {
1940             SCIP_EVENTHDLRDATA* eventhdlrdata_mipnodefocus;
1941             SCIP_EVENTHDLRDATA* eventhdlrdata_upperbound;
1942 
1943             /* because the subproblems could be reused in the copy, the event handler is not created again. If the
1944              * threadsafe is TRUE, then it is assumed that the subproblems are not reused.
1945              * NOTE: This currently works with the benders_default implementation. It may not be very general. */
1946             if( benders->benderssolvesubconvex == NULL && benders->benderssolvesub == NULL
1947                && (!benders->iscopy || benders->threadsafe) )
1948             {
1949                SCIP_CALL( SCIPallocBlockMemory(subproblem, &eventhdlrdata_mipnodefocus) );
1950                SCIP_CALL( SCIPallocBlockMemory(subproblem, &eventhdlrdata_upperbound) );
1951 
1952                SCIP_CALL( initEventhandlerData(subproblem, eventhdlrdata_mipnodefocus) );
1953                SCIP_CALL( initEventhandlerData(subproblem, eventhdlrdata_upperbound) );
1954 
1955                /* include the first LP solved event handler into the subproblem */
1956                SCIP_CALL( SCIPincludeEventhdlrBasic(subproblem, &eventhdlr, MIPNODEFOCUS_EVENTHDLR_NAME,
1957                      MIPNODEFOCUS_EVENTHDLR_DESC, eventExecBendersMipnodefocus, eventhdlrdata_mipnodefocus) );
1958                SCIP_CALL( SCIPsetEventhdlrInitsol(subproblem, eventhdlr, eventInitsolBendersMipnodefocus) );
1959                SCIP_CALL( SCIPsetEventhdlrExitsol(subproblem, eventhdlr, eventExitsolBendersMipnodefocus) );
1960                SCIP_CALL( SCIPsetEventhdlrExit(subproblem, eventhdlr, eventExitBendersMipnodefocus) );
1961                SCIP_CALL( SCIPsetEventhdlrFree(subproblem, eventhdlr, eventFreeBendersMipnodefocus) );
1962                assert(eventhdlr != NULL);
1963 
1964                /* include the upper bound interrupt event handler into the subproblem */
1965                SCIP_CALL( SCIPincludeEventhdlrBasic(subproblem, &eventhdlr, UPPERBOUND_EVENTHDLR_NAME,
1966                      UPPERBOUND_EVENTHDLR_DESC, eventExecBendersUpperbound, eventhdlrdata_upperbound) );
1967                SCIP_CALL( SCIPsetEventhdlrInitsol(subproblem, eventhdlr, eventInitsolBendersUpperbound) );
1968                SCIP_CALL( SCIPsetEventhdlrExitsol(subproblem, eventhdlr, eventExitsolBendersUpperbound) );
1969                SCIP_CALL( SCIPsetEventhdlrExit(subproblem, eventhdlr, eventExitBendersUpperbound) );
1970                SCIP_CALL( SCIPsetEventhdlrFree(subproblem, eventhdlr, eventFreeBendersUpperbound) );
1971                assert(eventhdlr != NULL);
1972             }
1973          }
1974       }
1975    }
1976 
1977    /* checking the convexity of the master problem. This information is useful for the cut generation methods, such as
1978     * non-good and integer cuts
1979     */
1980    SCIP_CALL( checkSubproblemConvexity(benders, set, -1) );
1981 
1982    benders->subprobscreated = TRUE;
1983 
1984    return SCIP_OKAY;
1985 }
1986 
1987 
1988 /** initializes Benders' decomposition */
SCIPbendersInit(SCIP_BENDERS * benders,SCIP_SET * set)1989 SCIP_RETCODE SCIPbendersInit(
1990    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
1991    SCIP_SET*             set                 /**< global SCIP settings */
1992    )
1993 {
1994    int i;
1995 
1996    assert(benders != NULL);
1997    assert(set != NULL);
1998 
1999    if( benders->initialized )
2000    {
2001       SCIPerrorMessage("Benders' decomposition <%s> already initialized\n", benders->name);
2002       return SCIP_INVALIDCALL;
2003    }
2004 
2005    if( set->misc_resetstat )
2006    {
2007       SCIPclockReset(benders->setuptime);
2008       SCIPclockReset(benders->bendersclock);
2009 
2010       benders->ncalls = 0;
2011       benders->ncutsfound = 0;
2012       benders->ntransferred = 0;
2013    }
2014 
2015    /* start timing */
2016    SCIPclockStart(benders->setuptime, set);
2017 
2018    if( benders->bendersinit != NULL )
2019    {
2020       SCIP_CALL( benders->bendersinit(set->scip, benders) );
2021    }
2022 
2023    benders->initialized = TRUE;
2024 
2025    /* if the Benders' decomposition is a copy, then the auxiliary variables already exist. So they are registered with
2026     * the Benders' decomposition struct during the init stage. If the Benders' decomposition is not a copy, then the
2027     * auxiliary variables need to be created, which occurs in the initpre stage
2028     */
2029    if( benders->iscopy )
2030    {
2031       /* the copied auxiliary variables must be assigned to the target Benders' decomposition */
2032       SCIP_CALL( assignAuxiliaryVariables(set->scip, benders) );
2033    }
2034 
2035    /* creates the subproblems and sets up the probing mode for LP subproblems. This function calls the benderscreatesub
2036     * callback. */
2037    SCIP_CALL( createSubproblems(benders, set) );
2038 
2039    /* storing the solution tolerance set by the SCIP parameters */
2040    SCIP_CALL( SCIPsetGetRealParam(set, "benders/solutiontol", &benders->solutiontol) );
2041 
2042    /* allocating memory for the stored constraints array */
2043    if( benders->storedcutssize == 0 )
2044    {
2045       SCIP_ALLOC( BMSallocBlockMemoryArray(SCIPblkmem(set->scip), &benders->storedcuts, BENDERS_ARRAYSIZE) );
2046       benders->storedcutssize = BENDERS_ARRAYSIZE;
2047       benders->nstoredcuts = 0;
2048    }
2049 
2050    /* initialising the Benders' cuts */
2051    SCIPbendersSortBenderscuts(benders);
2052    for( i = 0; i < benders->nbenderscuts; i++ )
2053    {
2054       SCIP_CALL( SCIPbenderscutInit(benders->benderscuts[i], set) );
2055    }
2056 
2057    /* stop timing */
2058    SCIPclockStop(benders->setuptime, set);
2059 
2060    return SCIP_OKAY;
2061 }
2062 
2063 
2064 /** Transfers Benders' cuts that were generated while solving a sub-SCIP to the original SCIP instance. This involves
2065  *  creating a constraint/cut that is equivalent to the generated cut in the sub-SCIP. This new constraint/cut is then
2066  *  added to the original SCIP instance.
2067  */
2068 static
createAndAddTransferredCut(SCIP * sourcescip,SCIP_BENDERS * benders,SCIP_VAR ** vars,SCIP_Real * vals,SCIP_Real lhs,SCIP_Real rhs,int nvars)2069 SCIP_RETCODE createAndAddTransferredCut(
2070    SCIP*                 sourcescip,         /**< the source SCIP from when the Benders' decomposition was copied */
2071    SCIP_BENDERS*         benders,            /**< the Benders' decomposition structure of the sub SCIP */
2072    SCIP_VAR**            vars,               /**< the variables from the source constraint */
2073    SCIP_Real*            vals,               /**< the coefficients of the variables in the source constriant */
2074    SCIP_Real             lhs,                /**< the LHS of the source constraint */
2075    SCIP_Real             rhs,                /**< the RHS of the source constraint */
2076    int                   nvars               /**< the number of variables in the source constraint */
2077    )
2078 {
2079    SCIP_BENDERS* sourcebenders;     /* the Benders' decomposition of the source SCIP */
2080    SCIP_CONSHDLR* consbenders;      /* a helper variable for the Benders' decomposition constraint handler */
2081    SCIP_CONS* transfercons = NULL;  /* the constraint that is generated to transfer the constraints/cuts */
2082    SCIP_ROW* transfercut = NULL;    /* the cut that is generated to transfer the constraints/cuts */
2083    SCIP_VAR* sourcevar;             /* the source variable that will be added to the transferred cut */
2084    SCIP_VAR* origvar;
2085    SCIP_Real scalar;
2086    SCIP_Real constant;
2087    char cutname[SCIP_MAXSTRLEN];    /* the name of the transferred cut */
2088    int i;
2089    SCIP_Bool fail;
2090 
2091    assert(sourcescip != NULL);
2092    assert(benders != NULL);
2093    assert(vars != NULL);
2094    assert(vals != NULL);
2095 
2096    /* retrieving the source Benders' decomposition structure */
2097    sourcebenders = SCIPfindBenders(sourcescip, SCIPbendersGetName(benders));
2098 
2099    /* retrieving the Benders' decomposition constraint handler */
2100    consbenders = SCIPfindConshdlr(sourcescip, "benders");
2101 
2102    /* setting the name of the transferred cut */
2103    (void) SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "transferredcut_%d",
2104       SCIPbendersGetNTransferredCuts(sourcebenders) );
2105 
2106    /* TODO: It could be more efficient to pass an updated vars array with the vals array to the
2107     * SCIPcreateConsBasicLinear/SCIPcreateEmptyRowConshdlr. This should be implemented to improve the performance of the
2108     * Large Neighbourhood Benders Search.
2109     */
2110 
2111    /* creating an empty row/constraint for the transferred cut */
2112    if( sourcebenders->cutsasconss )
2113    {
2114       SCIP_CALL( SCIPcreateConsBasicLinear(sourcescip, &transfercons, cutname, 0, NULL, NULL, lhs, rhs) );
2115       SCIP_CALL( SCIPsetConsRemovable(sourcescip, transfercons, TRUE) );
2116    }
2117    else
2118    {
2119       SCIP_CALL( SCIPcreateEmptyRowConshdlr(sourcescip, &transfercut, consbenders, cutname, lhs, rhs, FALSE,
2120             FALSE, TRUE) );
2121    }
2122 
2123    fail = FALSE;
2124    for( i = 0; i < nvars; i++ )
2125    {
2126       /* getting the original variable for the transformed variable */
2127       origvar = vars[i];
2128       scalar = 1.0;
2129       constant = 0.0;
2130       SCIP_CALL( SCIPvarGetOrigvarSum(&origvar, &scalar, &constant) );
2131 
2132       /* getting the source var from the hash map */
2133       sourcevar = (SCIP_VAR*) SCIPhashmapGetImage(benders->mastervarsmap, origvar);
2134 
2135       /* if the source variable is not found, then the mapping in incomplete. So the constraint can not be
2136        * transferred. */
2137       if( sourcevar == NULL )
2138       {
2139          fail = TRUE;
2140          break;
2141       }
2142 
2143       if( sourcebenders->cutsasconss )
2144       {
2145          assert( transfercons != NULL );
2146          SCIP_CALL( SCIPaddCoefLinear(sourcescip, transfercons, sourcevar, vals[i]) );    /*lint !e644*/
2147       }
2148       else
2149       {
2150          assert( transfercut != NULL );
2151          SCIP_CALL( SCIPaddVarToRow(sourcescip, transfercut, sourcevar, vals[i]) );       /*lint !e644*/
2152       }
2153    }
2154 
2155    /* if all of the source variables were found to generate the cut */
2156    if( !fail )
2157    {
2158       if( sourcebenders->cutsasconss )
2159       {
2160          SCIP_CALL( SCIPaddCons(sourcescip, transfercons) );
2161       }
2162       else
2163       {
2164          SCIP_CALL( SCIPaddPoolCut(sourcescip, transfercut) );
2165       }
2166 
2167       sourcebenders->ntransferred++;
2168    }
2169 
2170    /* release the row/constraint */
2171    if( sourcebenders->cutsasconss )
2172    {
2173       /* only release if the creation of the constraint failed. */
2174       SCIP_CALL( SCIPreleaseCons(sourcescip, &transfercons) );
2175    }
2176    else
2177    {
2178       SCIP_CALL( SCIPreleaseRow(sourcescip, &transfercut) );
2179    }
2180 
2181    return SCIP_OKAY;
2182 }
2183 
2184 
2185 /** transfers the cuts generated in a subscip to the source scip */
2186 static
transferBendersCuts(SCIP * sourcescip,SCIP * subscip,SCIP_BENDERS * benders)2187 SCIP_RETCODE transferBendersCuts(
2188    SCIP*                 sourcescip,         /**< the source SCIP from when the Benders' decomposition was copied */
2189    SCIP*                 subscip,            /**< the sub SCIP where the Benders' cuts were generated */
2190    SCIP_BENDERS*         benders             /**< the Benders' decomposition structure of the sub SCIP */
2191    )
2192 {
2193    SCIP_BENDERS* sourcebenders;     /* the Benders' decomposition of the source SCIP */
2194    SCIP_VAR** vars;                 /* the variables of the added constraint/row */
2195    SCIP_Real* vals;                 /* the values of the added constraint/row */
2196    SCIP_Real lhs;                   /* the LHS of the added constraint/row */
2197    SCIP_Real rhs;                   /* the RHS of the added constraint/row */
2198    int naddedcuts;
2199    int nvars;
2200    int i;
2201 
2202    assert(subscip != NULL);
2203    assert(benders != NULL);
2204 
2205    /* retrieving the source Benders' decomposition structure */
2206    sourcebenders = SCIPfindBenders(sourcescip, SCIPbendersGetName(benders));
2207 
2208    /* exit if the cuts should not be transferred from the sub SCIP to the source SCIP. */
2209    if( !sourcebenders->transfercuts || benders->mastervarsmap == NULL )
2210       return SCIP_OKAY;
2211 
2212    /* retrieving the number of stored Benders' cuts */
2213    naddedcuts =  SCIPbendersGetNStoredCuts(benders);
2214 
2215    /* looping over all added cuts to construct the cut for the source scip */
2216    for( i = 0; i < naddedcuts; i++ )
2217    {
2218       /* collecting the variable information from the constraint */
2219       SCIP_CALL( SCIPbendersGetStoredCutData(benders, i, &vars, &vals, &lhs, &rhs, &nvars) );
2220 
2221       if( nvars > 0 )
2222       {
2223          /* create and add the cut to be transferred from the sub SCIP to the source SCIP */
2224          SCIP_CALL( createAndAddTransferredCut(sourcescip, benders, vars, vals, lhs, rhs, nvars) );
2225       }
2226    }
2227 
2228    return SCIP_OKAY;
2229 }
2230 
2231 
2232 /** calls exit method of Benders' decomposition */
SCIPbendersExit(SCIP_BENDERS * benders,SCIP_SET * set)2233 SCIP_RETCODE SCIPbendersExit(
2234    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
2235    SCIP_SET*             set                 /**< global SCIP settings */
2236    )
2237 {
2238    int nsubproblems;
2239    int i;
2240 
2241    assert(benders != NULL);
2242    assert(set != NULL);
2243 
2244    if( !benders->initialized )
2245    {
2246       SCIPerrorMessage("Benders' decomposition <%s> not initialized\n", benders->name);
2247       return SCIP_INVALIDCALL;
2248    }
2249 
2250    /* start timing */
2251    SCIPclockStart(benders->setuptime, set);
2252 
2253    if( benders->bendersexit != NULL )
2254    {
2255       SCIP_CALL( benders->bendersexit(set->scip, benders) );
2256    }
2257 
2258    /* if the Benders' decomposition is a copy, then is a variable mapping was provided, then the generated cuts will
2259     * be transferred to the source scip
2260     */
2261    if( benders->iscopy && benders->mastervarsmap != NULL )
2262    {
2263       SCIP_CALL( transferBendersCuts(benders->sourcescip, set->scip, benders) );
2264    }
2265 
2266    /* releasing the stored constraints */
2267    for( i = benders->nstoredcuts - 1; i >= 0; i-- )
2268    {
2269       SCIPfreeBlockMemoryArray(set->scip, &benders->storedcuts[i]->vals, benders->storedcuts[i]->nvars);
2270       SCIPfreeBlockMemoryArray(set->scip, &benders->storedcuts[i]->vars, benders->storedcuts[i]->nvars);
2271       SCIPfreeBlockMemory(set->scip, &benders->storedcuts[i]); /*lint !e866*/
2272    }
2273 
2274    BMSfreeBlockMemoryArray(SCIPblkmem(set->scip), &benders->storedcuts, benders->storedcutssize);
2275    benders->storedcutssize = 0;
2276    benders->nstoredcuts = 0;
2277 
2278    /* releasing all of the auxiliary variables */
2279    nsubproblems = SCIPbendersGetNSubproblems(benders);
2280    for( i = 0; i < nsubproblems; i++ )
2281    {
2282       /* it is possible that the master problem is not solved. As such, the auxiliary variables will not be created. So
2283        * we don't need to release the variables
2284        */
2285       if( benders->auxiliaryvars[i] != NULL )
2286       {
2287          /* we need to remove the locks from the auxiliary variables. This will be called always for the highest priority
2288           * Benders' plugin and others if the auxiliary variables are not shared
2289           */
2290          if( !benders->iscopy && SCIPvarGetNLocksDown(benders->auxiliaryvars[i]) > 0 )
2291             SCIP_CALL( SCIPaddVarLocksType(set->scip, benders->auxiliaryvars[i], SCIP_LOCKTYPE_MODEL, -1, 0) );
2292 
2293          SCIP_CALL( SCIPreleaseVar(set->scip, &benders->auxiliaryvars[i]) );
2294       }
2295    }
2296 
2297    /* if a corepoint has been used for cut strengthening, then this needs to be freed */
2298    if( benders->corepoint != NULL )
2299    {
2300       SCIP_CALL( SCIPfreeSol(set->scip, &benders->corepoint) );
2301    }
2302 
2303    /* calling the exit method for the Benders' cuts */
2304    SCIPbendersSortBenderscuts(benders);
2305    for( i = 0; i < benders->nbenderscuts; i++ )
2306    {
2307       SCIP_CALL( SCIPbenderscutExit(benders->benderscuts[i], set) );
2308    }
2309 
2310    benders->initialized = FALSE;
2311 
2312    /* stop timing */
2313    SCIPclockStop(benders->setuptime, set);
2314 
2315    return SCIP_OKAY;
2316 }
2317 
2318 /** Checks whether a subproblem is independent. */
2319 static
checkSubproblemIndependence(SCIP * scip,SCIP_BENDERS * benders)2320 SCIP_RETCODE checkSubproblemIndependence(
2321    SCIP*                 scip,               /**< the SCIP data structure */
2322    SCIP_BENDERS*         benders             /**< Benders' decomposition */
2323    )
2324 {
2325    SCIP_VAR** vars;
2326    int nvars;
2327    int nsubproblems;
2328    int i;
2329    int j;
2330 
2331    assert(scip != NULL);
2332    assert(benders != NULL);
2333 
2334    /* retrieving the master problem variables */
2335    SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL) );
2336 
2337    nsubproblems = SCIPbendersGetNSubproblems(benders);
2338 
2339    /* looping over all subproblems to check whether there exists at least one master problem variable */
2340    for( i = 0; i < nsubproblems; i++ )
2341    {
2342       SCIP_Bool independent = FALSE;
2343 
2344       /* if there are user defined solving or freeing functions, then it is not possible to declare the independence of
2345        * the subproblems.
2346        */
2347       if( benders->benderssolvesubconvex == NULL && benders->benderssolvesub == NULL
2348          && benders->bendersfreesub == NULL )
2349       {
2350          independent = TRUE;
2351 
2352          for( j = 0; j < nvars; j++ )
2353          {
2354             SCIP_VAR* subprobvar;
2355 
2356             /* getting the subproblem problem variable corresponding to the master problem variable */
2357             SCIP_CALL( SCIPgetBendersSubproblemVar(scip, benders, vars[j], &subprobvar, i) );
2358 
2359             /* if the subporblem variable is not NULL, then the subproblem depends on the master problem */
2360             if( subprobvar != NULL )
2361             {
2362                independent = FALSE;
2363                break;
2364             }
2365          }
2366 
2367          /* setting the independent flag */
2368          SCIPbendersSetSubproblemIsIndependent(benders, i, independent);
2369       }
2370    }
2371 
2372    return SCIP_OKAY;
2373 }
2374 
2375 /** informs the Benders' decomposition that the presolving process is being started */
SCIPbendersInitpre(SCIP_BENDERS * benders,SCIP_SET * set,SCIP_STAT * stat)2376 SCIP_RETCODE SCIPbendersInitpre(
2377    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
2378    SCIP_SET*             set,                /**< global SCIP settings */
2379    SCIP_STAT*            stat                /**< dynamic problem statistics */
2380    )
2381 {
2382    assert(benders != NULL);
2383    assert(set != NULL);
2384    assert(stat != NULL);
2385 
2386    /* if the Benders' decomposition is the original, then the auxiliary variables need to be created. If the Benders'
2387     * decomposition is a copy, then the auxiliary variables already exist. The assignment of the auxiliary variables
2388     * occurs in bendersInit
2389     */
2390    if( !benders->iscopy )
2391    {
2392       /* check the subproblem independence. This check is only performed if the user has not implemented a solve
2393        * subproblem function.
2394        */
2395       if( benders->benderssolvesubconvex == NULL && benders->benderssolvesub == NULL )
2396         SCIP_CALL( checkSubproblemIndependence(set->scip, benders) );
2397 
2398       /* adding the auxiliary variables to the master problem */
2399       SCIP_CALL( addAuxiliaryVariablesToMaster(set->scip, benders) );
2400    }
2401 
2402    /* call presolving initialization method of Benders' decomposition */
2403    if( benders->bendersinitpre != NULL )
2404    {
2405       /* start timing */
2406       SCIPclockStart(benders->setuptime, set);
2407 
2408       SCIP_CALL( benders->bendersinitpre(set->scip, benders) );
2409 
2410       /* stop timing */
2411       SCIPclockStop(benders->setuptime, set);
2412    }
2413 
2414    return SCIP_OKAY;
2415 }
2416 
2417 
2418 /** informs the Benders' decomposition that the presolving process has completed */
SCIPbendersExitpre(SCIP_BENDERS * benders,SCIP_SET * set,SCIP_STAT * stat)2419 SCIP_RETCODE SCIPbendersExitpre(
2420    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
2421    SCIP_SET*             set,                /**< global SCIP settings */
2422    SCIP_STAT*            stat                /**< dynamic problem statistics */
2423    )
2424 {
2425    assert(benders != NULL);
2426    assert(set != NULL);
2427    assert(stat != NULL);
2428 
2429    /* call presolving  deinitialization method of Benders' decomposition */
2430    if( benders->bendersexitpre != NULL )
2431    {
2432       /* start timing */
2433       SCIPclockStart(benders->setuptime, set);
2434 
2435       SCIP_CALL( benders->bendersexitpre(set->scip, benders) );
2436 
2437       /* stop timing */
2438       SCIPclockStop(benders->setuptime, set);
2439    }
2440 
2441    return SCIP_OKAY;
2442 }
2443 
2444 /** informs Benders' decomposition that the branch and bound process is being started */
SCIPbendersInitsol(SCIP_BENDERS * benders,SCIP_SET * set)2445 SCIP_RETCODE SCIPbendersInitsol(
2446    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
2447    SCIP_SET*             set                 /**< global SCIP settings */
2448    )
2449 {
2450    int i;
2451 
2452    assert(benders != NULL);
2453    assert(set != NULL);
2454 
2455    /* call solving process initialization method of Benders' decomposition */
2456    if( benders->bendersinitsol != NULL )
2457    {
2458       /* start timing */
2459       SCIPclockStart(benders->setuptime, set);
2460 
2461       SCIP_CALL( benders->bendersinitsol(set->scip, benders) );
2462 
2463       /* stop timing */
2464       SCIPclockStop(benders->setuptime, set);
2465    }
2466 
2467    /* calling the initsol method for the Benders' cuts */
2468    SCIPbendersSortBenderscuts(benders);
2469    for( i = 0; i < benders->nbenderscuts; i++ )
2470    {
2471       SCIP_CALL( SCIPbenderscutInitsol(benders->benderscuts[i], set) );
2472    }
2473 
2474    return SCIP_OKAY;
2475 }
2476 
2477 /** informs Benders' decomposition that the branch and bound process data is being freed */
SCIPbendersExitsol(SCIP_BENDERS * benders,SCIP_SET * set)2478 SCIP_RETCODE SCIPbendersExitsol(
2479    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
2480    SCIP_SET*             set                 /**< global SCIP settings */
2481    )
2482 {
2483    int nsubproblems;
2484    int i;
2485 
2486    assert(benders != NULL);
2487    assert(set != NULL);
2488 
2489    nsubproblems = SCIPbendersGetNSubproblems(benders);
2490    /* freeing all subproblems that are independent, this is because they have not bee freed during the subproblem
2491     * solving loop.
2492     */
2493    for( i = 0; i < nsubproblems; i++ )
2494    {
2495       if( SCIPbendersSubproblemIsIndependent(benders, i) )
2496       {
2497          /* disabling the independence of the subproblem so that it can be freed */
2498          SCIPbendersSetSubproblemIsIndependent(benders, i, FALSE);
2499 
2500          /* freeing the independent subproblem */
2501          SCIP_CALL( SCIPbendersFreeSubproblem(benders, set, i) );
2502       }
2503    }
2504 
2505    /* call solving process deinitialization method of Benders' decomposition */
2506    if( benders->bendersexitsol != NULL )
2507    {
2508       /* start timing */
2509       SCIPclockStart(benders->setuptime, set);
2510 
2511       SCIP_CALL( benders->bendersexitsol(set->scip, benders) );
2512 
2513       /* stop timing */
2514       SCIPclockStop(benders->setuptime, set);
2515    }
2516 
2517    /* sorting the Benders' decomposition cuts in order of priority. Only a single cut is generated for each subproblem
2518     * per solving iteration. This is particularly important in the case of the optimality and feasibility cuts. Since
2519     * these work on two different solutions to the subproblem, it is not necessary to generate both cuts. So, once the
2520     * feasibility cut is generated, then no other cuts will be generated.
2521     */
2522    SCIPbendersSortBenderscuts(benders);
2523 
2524    /* calling the exitsol method for the Benders' cuts */
2525    for( i = 0; i < benders->nbenderscuts; i++ )
2526    {
2527       SCIP_CALL( SCIPbenderscutExitsol(benders->benderscuts[i], set) );
2528    }
2529 
2530    return SCIP_OKAY;
2531 }
2532 
2533 /** activates Benders' decomposition such that it is called in LP solving loop */
SCIPbendersActivate(SCIP_BENDERS * benders,SCIP_SET * set,int nsubproblems)2534 SCIP_RETCODE SCIPbendersActivate(
2535    SCIP_BENDERS*         benders,            /**< the Benders' decomposition structure */
2536    SCIP_SET*             set,                /**< global SCIP settings */
2537    int                   nsubproblems        /**< the number subproblems used in this decomposition */
2538    )
2539 {
2540    SCIP_EVENTHDLR* eventhdlr;
2541    SCIP_EVENTHDLRDATA* eventhdlrdata;
2542    int i;
2543 
2544    assert(benders != NULL);
2545    assert(set != NULL);
2546    assert(set->stage == SCIP_STAGE_INIT || set->stage == SCIP_STAGE_PROBLEM);
2547 
2548    if( !benders->active )
2549    {
2550       benders->active = TRUE;
2551       set->nactivebenders++;
2552       set->benderssorted = FALSE;
2553 
2554       benders->nsubproblems = nsubproblems;
2555       benders->nactivesubprobs = nsubproblems;
2556       benders->prevlowerbound = -SCIPsetInfinity(set);
2557       benders->strengthenround = FALSE;
2558 
2559       /* allocating memory for the subproblems arrays */
2560       SCIP_ALLOC( BMSallocMemoryArray(&benders->subproblems, benders->nsubproblems) );
2561       SCIP_ALLOC( BMSallocMemoryArray(&benders->auxiliaryvars, benders->nsubproblems) );
2562       SCIP_ALLOC( BMSallocMemoryArray(&benders->solvestat, benders->nsubproblems) );
2563       SCIP_ALLOC( BMSallocMemoryArray(&benders->subprobobjval, benders->nsubproblems) );
2564       SCIP_ALLOC( BMSallocMemoryArray(&benders->bestsubprobobjval, benders->nsubproblems) );
2565       SCIP_ALLOC( BMSallocMemoryArray(&benders->subproblowerbound, benders->nsubproblems) );
2566       SCIP_ALLOC( BMSallocMemoryArray(&benders->subprobtype, benders->nsubproblems) );
2567       SCIP_ALLOC( BMSallocMemoryArray(&benders->subprobisconvex, benders->nsubproblems) );
2568       SCIP_ALLOC( BMSallocMemoryArray(&benders->subprobisnonlinear, benders->nsubproblems) );
2569       SCIP_ALLOC( BMSallocMemoryArray(&benders->subprobsetup, benders->nsubproblems) );
2570       SCIP_ALLOC( BMSallocMemoryArray(&benders->indepsubprob, benders->nsubproblems) );
2571       SCIP_ALLOC( BMSallocMemoryArray(&benders->subprobenabled, benders->nsubproblems) );
2572       SCIP_ALLOC( BMSallocMemoryArray(&benders->mastervarscont, benders->nsubproblems) );
2573 
2574       /* creating the priority queue for the subproblem solving status */
2575       SCIP_CALL( SCIPpqueueCreate(&benders->subprobqueue, benders->nsubproblems, 1.1,
2576             benders->benderssubcomp == NULL ? benderssubcompdefault : benders->benderssubcomp, NULL) );
2577 
2578       for( i = 0; i < benders->nsubproblems; i++ )
2579       {
2580          SCIP_SUBPROBLEMSOLVESTAT* solvestat;
2581 
2582          benders->subproblems[i] = NULL;
2583          benders->auxiliaryvars[i] = NULL;
2584          benders->subprobobjval[i] = SCIPsetInfinity(set);
2585          benders->bestsubprobobjval[i] = SCIPsetInfinity(set);
2586          benders->subproblowerbound[i] = -SCIPsetInfinity(set);
2587          benders->subprobtype[i] = SCIP_BENDERSSUBTYPE_UNKNOWN;
2588          benders->subprobisconvex[i] = FALSE;
2589          benders->subprobisnonlinear[i] = FALSE;
2590          benders->subprobsetup[i] = FALSE;
2591          benders->indepsubprob[i] = FALSE;
2592          benders->subprobenabled[i] = TRUE;
2593          benders->mastervarscont[i] = FALSE;
2594 
2595          /* initialising the subproblem solving status */
2596          SCIP_ALLOC( BMSallocMemory(&solvestat) );
2597          solvestat->idx = i;
2598          solvestat->ncalls = 0;
2599          solvestat->avgiter = 0;
2600          benders->solvestat[i] = solvestat;
2601 
2602          /* inserting the initial elements into the priority queue */
2603          SCIP_CALL( SCIPpqueueInsert(benders->subprobqueue, benders->solvestat[i]) );
2604       }
2605 
2606       /* adding an eventhandler for updating the lower bound when the root node is solved. */
2607       eventhdlrdata = (SCIP_EVENTHDLRDATA*)benders;
2608 
2609       /* include event handler into SCIP */
2610       SCIP_CALL( SCIPincludeEventhdlrBasic(set->scip, &eventhdlr, NODESOLVED_EVENTHDLR_NAME, NODESOLVED_EVENTHDLR_DESC,
2611             eventExecBendersNodesolved, eventhdlrdata) );
2612       SCIP_CALL( SCIPsetEventhdlrInitsol(set->scip, eventhdlr, eventInitsolBendersNodesolved) );
2613       assert(eventhdlr != NULL);
2614    }
2615 
2616    return SCIP_OKAY;
2617 }
2618 
2619 /** deactivates Benders' decomposition such that it is no longer called in LP solving loop */
SCIPbendersDeactivate(SCIP_BENDERS * benders,SCIP_SET * set)2620 SCIP_RETCODE SCIPbendersDeactivate(
2621    SCIP_BENDERS*         benders,            /**< the Benders' decomposition structure */
2622    SCIP_SET*             set                 /**< global SCIP settings */
2623    )
2624 {
2625    int i;
2626 
2627    assert(benders != NULL);
2628    assert(set != NULL);
2629    assert(set->stage == SCIP_STAGE_INIT || set->stage == SCIP_STAGE_PROBLEM);
2630 
2631    if( benders->active )
2632    {
2633       int nsubproblems;
2634 
2635       nsubproblems = SCIPbendersGetNSubproblems(benders);
2636 
2637 #ifndef NDEBUG
2638       /* checking whether the auxiliary variables and subproblems are all NULL */
2639       for( i = 0; i < nsubproblems; i++ )
2640          assert(benders->auxiliaryvars[i] == NULL);
2641 #endif
2642 
2643       /* if the subproblems were created by the Benders' decomposition core, then they need to be freed */
2644       if( benders->freesubprobs )
2645       {
2646          for( i = SCIPbendersGetNSubproblems(benders) - 1; i >= 0; i-- )
2647          {
2648             SCIP* subproblem = SCIPbendersSubproblem(benders, i);
2649             SCIP_CALL( SCIPfree(&subproblem) );
2650          }
2651       }
2652 
2653       benders->active = FALSE;
2654       set->nactivebenders--;
2655       set->benderssorted = FALSE;
2656 
2657       /* freeing the priority queue memory */
2658       SCIPpqueueFree(&benders->subprobqueue);
2659 
2660       for( i = nsubproblems - 1; i >= 0; i-- )
2661          BMSfreeMemory(&benders->solvestat[i]);
2662 
2663       /* freeing the memory allocated during the activation of the Benders' decomposition */
2664       BMSfreeMemoryArray(&benders->mastervarscont);
2665       BMSfreeMemoryArray(&benders->subprobenabled);
2666       BMSfreeMemoryArray(&benders->indepsubprob);
2667       BMSfreeMemoryArray(&benders->subprobsetup);
2668       BMSfreeMemoryArray(&benders->subprobisnonlinear);
2669       BMSfreeMemoryArray(&benders->subprobisconvex);
2670       BMSfreeMemoryArray(&benders->subprobtype);
2671       BMSfreeMemoryArray(&benders->subproblowerbound);
2672       BMSfreeMemoryArray(&benders->bestsubprobobjval);
2673       BMSfreeMemoryArray(&benders->subprobobjval);
2674       BMSfreeMemoryArray(&benders->auxiliaryvars);
2675       BMSfreeMemoryArray(&benders->solvestat);
2676       BMSfreeMemoryArray(&benders->subproblems);
2677    }
2678 
2679    return SCIP_OKAY;
2680 }
2681 
2682 /** returns whether the given Benders' decomposition is in use in the current problem */
SCIPbendersIsActive(SCIP_BENDERS * benders)2683 SCIP_Bool SCIPbendersIsActive(
2684    SCIP_BENDERS*         benders             /**< the Benders' decomposition structure */
2685    )
2686 {
2687    assert(benders != NULL);
2688 
2689    return benders->active;
2690 }
2691 
2692 /** updates the lower bound for all auxiliary variables. This is called if the first LP enforced is unbounded. */
2693 static
updateAuxiliaryVarLowerbound(SCIP_BENDERS * benders,SCIP_SET * set,SCIP_RESULT * result)2694 SCIP_RETCODE updateAuxiliaryVarLowerbound(
2695    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
2696    SCIP_SET*             set,                /**< global SCIP settings */
2697    SCIP_RESULT*          result              /**< the result from updating the auxiliary variable lower bound */
2698    )
2699 {
2700    int nsubproblems;
2701    int i;
2702 
2703    assert(benders != NULL);
2704    assert(set != NULL);
2705 
2706    (*result) = SCIP_DIDNOTRUN;
2707 
2708    nsubproblems = SCIPbendersGetNSubproblems(benders);
2709 
2710    for( i = 0; i < nsubproblems; i++ )
2711    {
2712       SCIP_VAR* auxiliaryvar;
2713       SCIP_Real lowerbound;
2714       SCIP_Bool infeasible;
2715 
2716       infeasible = FALSE;
2717 
2718       /* computing the lower bound of the subproblem by solving it without any variable fixings */
2719       SCIP_CALL( SCIPbendersComputeSubproblemLowerbound(benders, set, i, &lowerbound, &infeasible) );
2720 
2721       /* if the subproblem is infeasible, then the original problem is infeasible */
2722       if( infeasible )
2723       {
2724          (*result) = SCIP_INFEASIBLE;
2725          break;
2726       }
2727 
2728       /* retrieving the auxiliary variable */
2729       auxiliaryvar = SCIPbendersGetAuxiliaryVar(benders, i);
2730 
2731       /* only update the lower bound if it is greater than the current lower bound */
2732       if( SCIPsetIsGT(set, lowerbound, SCIPvarGetLbGlobal(auxiliaryvar)) )
2733       {
2734          SCIPsetDebugMsg(set, "Tightened lower bound of <%s> to %g\n", SCIPvarGetName(auxiliaryvar), lowerbound);
2735          /* updating the lower bound of the auxiliary variable */
2736          SCIP_CALL( SCIPchgVarLb(set->scip, auxiliaryvar, lowerbound) );
2737          (*result) = SCIP_REDUCEDDOM;
2738       }
2739 
2740       /* stores the lower bound for the subproblem */
2741       SCIPbendersUpdateSubproblemLowerbound(benders, i, lowerbound);
2742    }
2743 
2744    return SCIP_OKAY;
2745 }
2746 
2747 /** sets the core point used for cut strengthening. If the strenghtenintpoint is set to 'i', then the core point is
2748  *  reinitialised each time the incumbent is updated
2749  */
2750 static
setAndUpdateCorePoint(SCIP * scip,SCIP_BENDERS * benders)2751 SCIP_RETCODE setAndUpdateCorePoint(
2752    SCIP*                 scip,               /**< the SCIP data structure */
2753    SCIP_BENDERS*         benders             /**< Benders' decomposition */
2754    )
2755 {
2756    SCIP_SOL* bestsol;
2757 
2758    assert(scip != NULL);
2759    assert(benders != NULL);
2760 
2761    /* if the core point is not NULL and the interior point is not reinitialised, then nothing is done  */
2762    if( benders->corepoint != NULL && benders->strengthenintpoint != 'i' )
2763       return SCIP_OKAY;
2764 
2765    bestsol = SCIPgetBestSol(scip);
2766 
2767    /* if the core point should be updated, then this only happens if the incumbent solution has been updated */
2768    if( benders->strengthenintpoint == 'i' && benders->initcorepoint == bestsol )
2769       return SCIP_OKAY;
2770 
2771    /* if a corepoint has been used for cut strengthening, then this needs to be freed */
2772    if( benders->corepoint != NULL )
2773    {
2774       SCIP_CALL( SCIPfreeSol(scip, &benders->corepoint) );
2775    }
2776 
2777    switch( benders->strengthenintpoint )
2778    {
2779       SCIP_VAR** vars;
2780       SCIP_Real timelimit;
2781       int nvars;
2782       int i;
2783 
2784       case 'l':
2785          SCIP_CALL( SCIPcreateLPSol(scip, &benders->corepoint, NULL) );
2786          SCIP_CALL( SCIPunlinkSol(scip, benders->corepoint) );
2787          break;
2788       case 'f':
2789       case 'i':
2790          SCIP_CALL( SCIPcreateSolCopy(scip, &benders->corepoint, bestsol) );
2791          SCIP_CALL( SCIPunlinkSol(scip, benders->corepoint) );
2792          benders->initcorepoint = bestsol;
2793          break;
2794       case 'r':
2795          /* prepare time limit */
2796          SCIP_CALL( SCIPgetRealParam(scip, "limits/time", &timelimit) );
2797          if ( ! SCIPisInfinity(scip, timelimit) )
2798             timelimit -= SCIPgetSolvingTime(scip);
2799 
2800          /* if there is time remaining, then compute the relative interior point. Otherwise, return the LP solution */
2801          if ( timelimit > 0.0 )
2802          {
2803             SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, 0, "Computing relative interior point (time limit: %g, iter limit: %d) ...\n", timelimit, INT_MAX);
2804             SCIP_CALL( SCIPcomputeLPRelIntPoint(scip, TRUE, FALSE, timelimit, INT_MAX, &benders->corepoint) );
2805          }
2806          else
2807          {
2808             SCIP_CALL( SCIPcreateLPSol(scip, &benders->corepoint, NULL) );
2809             SCIP_CALL( SCIPunlinkSol(scip, benders->corepoint) );
2810          }
2811          break;
2812       case 'z':
2813          SCIP_CALL( SCIPcreateSol(scip, &benders->corepoint, NULL) );
2814          break;
2815       case 'o':
2816          SCIP_CALL( SCIPcreateSol(scip, &benders->corepoint, NULL) );
2817 
2818          /* getting the variable data so that the  */
2819          SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL) );
2820 
2821          /* setting all variable values to 1.0 */
2822          for( i = 0; i < nvars; i++ )
2823          {
2824             SCIP_CALL( SCIPsetSolVal(scip, benders->corepoint, vars[i], 1.0) );
2825          }
2826          break;
2827       default:
2828          SCIP_CALL( SCIPcreateLPSol(scip, &benders->corepoint, NULL) );
2829          SCIP_CALL( SCIPunlinkSol(scip, benders->corepoint) );
2830    }
2831 
2832    return SCIP_OKAY;
2833 }
2834 
2835 /** performs cut strengthening by using an interior solution to generate cuts */
2836 static
performInteriorSolCutStrengthening(SCIP_BENDERS * benders,SCIP_SET * set,SCIP_SOL * sol,SCIP_BENDERSENFOTYPE type,SCIP_Bool checkint,SCIP_Bool perturbsol,SCIP_Bool * auxviol,SCIP_Bool * infeasible,SCIP_Bool * skipsolve,SCIP_RESULT * result)2837 SCIP_RETCODE performInteriorSolCutStrengthening(
2838    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
2839    SCIP_SET*             set,                /**< global SCIP settings */
2840    SCIP_SOL*             sol,                /**< primal CIP solution */
2841    SCIP_BENDERSENFOTYPE  type,               /**< the type of solution being enforced */
2842    SCIP_Bool             checkint,           /**< are the subproblems called during a check/enforce of integer sols? */
2843    SCIP_Bool             perturbsol,         /**< should the solution be perturbed to escape infeasibility? */
2844    SCIP_Bool*            auxviol,            /**< set to TRUE only if the solution is feasible but the aux vars are violated */
2845    SCIP_Bool*            infeasible,         /**< is the master problem infeasible with respect to the Benders' cuts? */
2846    SCIP_Bool*            skipsolve,          /**< should the main solve be skipped as a result of this strengthening? */
2847    SCIP_RESULT*          result              /**< result of the pricing process */
2848    )
2849 {
2850    SCIP_SOL* sepapoint;
2851    SCIP_VAR** vars;
2852    int prevcutsfound;
2853    int nvars;
2854    int i;
2855 
2856    assert(benders != NULL);
2857    assert(set != NULL);
2858 
2859    (*result) = SCIP_DIDNOTRUN;
2860    (*skipsolve) = FALSE;
2861 
2862    /* the cut stabilisation is only performed when enforcing LP solutions. The solution is not NULL if the stabilisation
2863     * is currently being performed. It is important to avoid recursion
2864     */
2865    if( type != SCIP_BENDERSENFOTYPE_LP || sol != NULL )
2866       return SCIP_OKAY;
2867 
2868    /* checking if a change to the lower bound has occurred */
2869    if( SCIPsetIsGT(set, SCIPgetLowerbound(set->scip), benders->prevlowerbound)
2870       || SCIPgetCurrentNode(set->scip) != benders->prevnode )
2871    {
2872       benders->prevnode = SCIPgetCurrentNode(set->scip);
2873       benders->prevlowerbound = SCIPgetLowerbound(set->scip);
2874       benders->noimprovecount = 0;
2875    }
2876    else
2877       benders->noimprovecount++;
2878 
2879    /* if the number of iterations without improvement exceeds 3*noimprovelimit, then the no stabilisation is performed
2880     */
2881    if( benders->noimprovecount > 3*benders->noimprovelimit )
2882       return SCIP_OKAY;
2883 
2884    /* if there is no incumbent solution, then it is not possible to create the core point and hence the strengthening
2885     * can not be performed
2886     */
2887    if( SCIPgetBestSol(set->scip) == NULL )
2888       return SCIP_OKAY;
2889 
2890    /* if no LP iterations have been performed since the last call of the cut strenghtening, then the strengthening is
2891     * aborted
2892     */
2893    if( benders->prevnlpiter == SCIPgetNLPIterations(set->scip) )
2894       return SCIP_OKAY;
2895 
2896    benders->prevnlpiter = SCIPgetNLPIterations(set->scip);
2897 
2898    /* if the separation point solution is NULL, then we create the solution using the current LP relaxation. */
2899    SCIP_CALL( setAndUpdateCorePoint(set->scip, benders) );
2900 
2901    /* creating the separation point
2902     * TODO: This could be a little to memory heavy, it may be better just to create the separation point once and then
2903     * update it each time.
2904     */
2905    SCIP_CALL( SCIPcreateLPSol(set->scip, &sepapoint, NULL) );
2906    SCIP_CALL( SCIPunlinkSol(set->scip, sepapoint) );
2907 
2908    SCIP_CALL( SCIPgetVarsData(set->scip, &vars, &nvars, NULL, NULL, NULL, NULL) );
2909    assert(vars != NULL);
2910 
2911    /* creating a solution that is a convex combination of the LP solution and the separation point */
2912    for( i = 0; i < nvars; i++ )
2913    {
2914       SCIP_VAR* subvar;
2915       SCIP_Real corepointval;
2916       SCIP_Real lpsolval;
2917       SCIP_Real newsolval;
2918       int j;
2919 
2920       corepointval = SCIPgetSolVal(set->scip, benders->corepoint, vars[i]);
2921       lpsolval = SCIPgetSolVal(set->scip, sol, vars[i]);
2922       newsolval = lpsolval;
2923 
2924       /* checking whether the master variable is mapped to any subproblem variables */
2925       subvar = NULL;
2926       j = 0;
2927       while( subvar == NULL && j < SCIPgetBendersNSubproblems(set->scip, benders)  )
2928       {
2929          SCIP_CALL( SCIPgetBendersSubproblemVar(set->scip, benders, vars[i], &subvar, j) );
2930          j++;
2931       }
2932 
2933       /* if the variable is a linking variable and it is not fixed, then a convex combination with the corepoint is
2934        * computed.
2935        */
2936       if( subvar != NULL && SCIPvarGetStatus(vars[i]) != SCIP_VARSTATUS_FIXED )
2937       {
2938          /* if the number of iterations without improvement exceeds noimprovelimit, then no convex combination is
2939           * created
2940           */
2941          if( !perturbsol && benders->noimprovecount <= benders->noimprovelimit )
2942          {
2943             newsolval = lpsolval*benders->convexmult + corepointval*(1 - benders->convexmult);
2944 
2945             /* updating the core point */
2946             SCIP_CALL( SCIPsetSolVal(set->scip, benders->corepoint, vars[i], newsolval) );
2947          }
2948 
2949          /* if the number of iterations without improvement is less than 2*noimprovelimit, then perturbation is
2950           * performed
2951           */
2952          if( perturbsol || benders->noimprovecount <= 2*benders->noimprovelimit )
2953             newsolval += benders->perturbeps;
2954       }
2955 
2956       /* updating the separation point */
2957       SCIP_CALL( SCIPsetSolVal(set->scip, sepapoint, vars[i], newsolval) );
2958    }
2959 
2960    /* storing the number of cuts found */
2961    prevcutsfound = SCIPbendersGetNCutsFound(benders);
2962 
2963    SCIPsetDebugMsg(set, "solving Benders' decomposition subproblems with stabilised point.\n");
2964 
2965    /* calling the subproblem solving method to generate cuts from the separation solution */
2966    SCIP_CALL( SCIPsolveBendersSubproblems(set->scip, benders, sepapoint, result, infeasible, auxviol, type, checkint) );
2967 
2968    SCIPsetDebugMsg(set, "solved Benders' decomposition subproblems with stabilised point. noimprovecount %d result %d\n",
2969       benders->noimprovecount, (*result));
2970 
2971    /* if constraints were added, then the main Benders' solving loop is skipped. */
2972    if( !(*infeasible) && ((*result) == SCIP_CONSADDED || (*result) == SCIP_SEPARATED) )
2973       (*skipsolve) = TRUE;
2974 
2975    /* capturing cut strengthening statistics */
2976    benders->nstrengthencalls++;
2977    benders->nstrengthencuts += (SCIPbendersGetNCutsFound(benders) - prevcutsfound);
2978 
2979    /* if no cuts were added, then the strengthening round is marked as failed */
2980    if( SCIPbendersGetNCutsFound(benders) == prevcutsfound )
2981       benders->nstrengthenfails++;
2982 
2983    /* freeing the sepapoint solution */
2984    SCIP_CALL( SCIPfreeSol(set->scip, &sepapoint) );
2985 
2986    return SCIP_OKAY;
2987 }
2988 
2989 
2990 /** Returns whether only the convex relaxations will be checked in this solve loop
2991  *  when Benders' is used in the LNS heuristics, only the convex relaxations of the master/subproblems are checked,
2992  *  i.e. no integer cuts are generated. In this case, then Benders' decomposition is performed under the assumption
2993  *  that all subproblems are convex relaxations.
2994  */
SCIPbendersOnlyCheckConvexRelax(SCIP_BENDERS * benders,SCIP_Bool subscipsoff)2995 SCIP_Bool SCIPbendersOnlyCheckConvexRelax(
2996    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
2997    SCIP_Bool             subscipsoff         /**< flag indicating whether plugins using sub-SCIPs are deactivated */
2998    )
2999 {
3000    return benders->iscopy && benders->lnscheck && subscipsoff;
3001 }
3002 
3003 /** returns the number of subproblems that will be checked in this iteration */
3004 static
numSubproblemsToCheck(SCIP_BENDERS * benders,SCIP_SET * set,SCIP_BENDERSENFOTYPE type)3005 int numSubproblemsToCheck(
3006    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
3007    SCIP_SET*             set,                /**< global SCIP settings */
3008    SCIP_BENDERSENFOTYPE  type                /**< the type of solution being enforced */
3009    )
3010 {
3011    if( benders->ncalls == 0 || type == SCIP_BENDERSENFOTYPE_CHECK
3012       || SCIPbendersOnlyCheckConvexRelax(benders, SCIPsetGetSubscipsOff(set)) )
3013       return SCIPbendersGetNSubproblems(benders);
3014    else
3015       return (int) SCIPsetCeil(set, (SCIP_Real) SCIPbendersGetNSubproblems(benders)*benders->subprobfrac);
3016 }
3017 
3018 /** returns whether the solving of the given subproblem needs to be executed */
3019 static
subproblemIsActive(SCIP_BENDERS * benders,int probnumber)3020 SCIP_Bool subproblemIsActive(
3021    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
3022    int                   probnumber          /**< the subproblem index */
3023    )
3024 {
3025    return (!SCIPbendersSubproblemIsIndependent(benders, probnumber)
3026       && SCIPbendersSubproblemIsEnabled(benders, probnumber));
3027 }
3028 
3029 /** creates an ordered list of subproblem indices to be solved */
3030 static
createSolveSubproblemIndexList(SCIP_BENDERS * benders,SCIP_SET * set,SCIP_BENDERSENFOTYPE type,int ** solveidx,int * nsolveidx)3031 void createSolveSubproblemIndexList(
3032    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
3033    SCIP_SET*             set,                /**< global SCIP settings */
3034    SCIP_BENDERSENFOTYPE  type,               /**< the type of solution being enforced */
3035    int**                 solveidx,           /**< a list of subproblem indices to the solved in the current iteration */
3036    int*                  nsolveidx           /**< the number of subproblem indices in the list */
3037    )
3038 {
3039    int nsubproblems;
3040    int numtocheck;
3041    int subproblemcount;
3042 
3043    assert(benders != NULL);
3044    assert(set != NULL);
3045    assert((*solveidx) != NULL);
3046    assert(nsolveidx != NULL);
3047    assert(SCIPpqueueNElems(benders->subprobqueue) <= SCIPbendersGetNSubproblems(benders));
3048 
3049    nsubproblems = SCIPbendersGetNSubproblems(benders);
3050 
3051    /* it is possible to only solve a subset of subproblems. This is given by a parameter. */
3052    numtocheck = numSubproblemsToCheck(benders, set, type);
3053 
3054    (*nsolveidx) = 0;
3055 
3056    subproblemcount = 0;
3057    while( subproblemcount < nsubproblems && subproblemcount < numtocheck )
3058    {
3059       SCIP_SUBPROBLEMSOLVESTAT* solvestat;
3060 
3061       solvestat = (SCIP_SUBPROBLEMSOLVESTAT*)SCIPpqueueRemove(benders->subprobqueue);
3062       (*solveidx)[(*nsolveidx)] = solvestat->idx;
3063       (*nsolveidx)++;
3064 
3065       subproblemcount++;
3066    }
3067 }
3068 
3069 /** updates the subproblem solving statistics and inserts the indices into the queue */
3070 static
updateSubproblemStatQueue(SCIP_BENDERS * benders,int * solveidx,int nsolveidx,SCIP_Bool updatestat)3071 SCIP_RETCODE updateSubproblemStatQueue(
3072    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
3073    int*                  solveidx,           /**< the list of indices of subproblems that were solved */
3074    int                   nsolveidx,          /**< the number of subproblem indices */
3075    SCIP_Bool             updatestat          /**< should the statistics be updated */
3076    )
3077 {
3078    int i;
3079 
3080    assert(benders != NULL);
3081    assert(solveidx != NULL);
3082 
3083    for( i = 0; i < nsolveidx; i++ )
3084    {
3085       SCIP* subproblem;
3086       SCIP_SUBPROBLEMSOLVESTAT* solvestat;
3087 
3088       subproblem = SCIPbendersSubproblem(benders, solveidx[i]);
3089       solvestat = benders->solvestat[solveidx[i]];
3090       assert(solvestat->idx == solveidx[i]);
3091 
3092       /* updating the solving statistics */
3093       if( updatestat )
3094       {
3095          if( subproblem == NULL )
3096             solvestat->avgiter = 1;
3097          else
3098             solvestat->avgiter = (SCIP_Real)(solvestat->avgiter*solvestat->ncalls + SCIPgetNLPIterations(subproblem))
3099                /(SCIP_Real)(solvestat->ncalls + 1);
3100          solvestat->ncalls++;
3101       }
3102 
3103       /* inserting the solving statistics into the priority queue */
3104       SCIP_CALL( SCIPpqueueInsert(benders->subprobqueue, solvestat) );
3105    }
3106 
3107    assert(SCIPpqueueNElems(benders->subprobqueue) == SCIPbendersGetNSubproblems(benders));
3108 
3109    return SCIP_OKAY;
3110 }
3111 
3112 /** Solves each of the Benders' decomposition subproblems for the given solution. All, or a fraction, of subproblems are
3113  *  solved before the Benders' decomposition cuts are generated.
3114  *  Since a convex relaxation of the subproblem could be solved to generate cuts, a parameter nverified is used to
3115  *  identified the number of subproblems that have been solved in their "original" form. For example, if the subproblem
3116  *  is a MIP, then if the LP is solved to generate cuts, this does not constitute a verification. The verification is
3117  *  only performed when the MIP is solved.
3118  */
3119 static
solveBendersSubproblems(SCIP_BENDERS * benders,SCIP_SET * set,SCIP_SOL * sol,SCIP_BENDERSENFOTYPE type,SCIP_BENDERSSOLVELOOP solveloop,SCIP_Bool checkint,int * nverified,int * solveidx,int nsolveidx,SCIP_Bool ** subprobsolved,SCIP_BENDERSSUBSTATUS ** substatus,SCIP_Bool * infeasible,SCIP_Bool * optimal,SCIP_Bool * stopped)3120 SCIP_RETCODE solveBendersSubproblems(
3121    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
3122    SCIP_SET*             set,                /**< global SCIP settings */
3123    SCIP_SOL*             sol,                /**< primal CIP solution */
3124    SCIP_BENDERSENFOTYPE  type,               /**< the type of solution being enforced */
3125    SCIP_BENDERSSOLVELOOP solveloop,          /**< the current solve loop */
3126    SCIP_Bool             checkint,           /**< are the subproblems called during a check/enforce of integer sols? */
3127    int*                  nverified,          /**< the number of subproblems verified in the current loop */
3128    int*                  solveidx,           /**< the indices of subproblems to be solved in this loop */
3129    int                   nsolveidx,          /**< the number of subproblems to be solved in this loop */
3130    SCIP_Bool**           subprobsolved,      /**< an array indicating the subproblems that were solved in this loop. */
3131    SCIP_BENDERSSUBSTATUS** substatus,        /**< array to store the status of the subsystem */
3132    SCIP_Bool*            infeasible,         /**< is the master problem infeasible with respect to the Benders' cuts? */
3133    SCIP_Bool*            optimal,            /**< is the current solution optimal? */
3134    SCIP_Bool*            stopped             /**< was the solving process stopped? */
3135    )
3136 {
3137    SCIP_Bool onlyconvexcheck;
3138 #ifdef _OPENMP
3139    int numthreads;
3140    int maxnthreads;
3141 #endif
3142    int i;
3143    int j;
3144 
3145    /* local variables for parallelisation of the solving loop */
3146    int locnverified = *nverified;
3147    SCIP_Bool locinfeasible = *infeasible;
3148    SCIP_Bool locoptimal = *optimal;
3149    SCIP_Bool locstopped = *stopped;
3150 
3151    SCIP_RETCODE retcode = SCIP_OKAY;
3152 
3153    assert(benders != NULL);
3154    assert(set != NULL);
3155 
3156    /* getting the number of threads to use when solving the subproblems. This will be either be
3157     * min(numthreads, maxnthreads).
3158     * NOTE: This may not be correct. The Benders' decomposition parallelisation should not take all minimum threads if
3159     * they are specified. The number of threads should be specified with the Benders' decomposition parameters.
3160     */
3161 #ifdef _OPENMP
3162    SCIP_CALL( SCIPsetGetIntParam(set, "parallel/maxnthreads", &maxnthreads) );
3163    numthreads = MIN(benders->numthreads, maxnthreads);
3164 #endif
3165 
3166    /* in the case of an LNS check, only the convex relaxations of the subproblems will be solved. This is a performance
3167     * feature, since solving the convex relaxation is typically much faster than solving the corresponding CIP. While
3168     * the CIP is not solved during the LNS check, the solutions are still of higher quality than when Benders' is not
3169     * employed.
3170     */
3171    onlyconvexcheck = SCIPbendersOnlyCheckConvexRelax(benders, SCIPsetGetSubscipsOff(set));
3172 
3173    SCIPsetDebugMsg(set, "Performing the subproblem solving process. Number of subproblems to check %d\n", nsolveidx);
3174 
3175    SCIPsetDebugMsg(set, "Benders' decomposition - solve loop %d\n", solveloop);
3176 
3177    if( type == SCIP_BENDERSENFOTYPE_CHECK && sol == NULL )
3178    {
3179       /* TODO: Check whether this is absolutely necessary. I think that this if statment can be removed. */
3180       locinfeasible = TRUE;
3181    }
3182    else
3183    {
3184       /* solving each of the subproblems for Benders' decomposition */
3185       /* TODO: ensure that the each of the subproblems solve and update the parameters with the correct return values
3186        */
3187 #ifndef __INTEL_COMPILER
3188       #pragma omp parallel for num_threads(numthreads) private(i) reduction(&&:locoptimal) reduction(||:locinfeasible) reduction(+:locnverified) reduction(||:locstopped) reduction(min:retcode)
3189 #endif
3190       for( j = 0; j < nsolveidx; j++ )
3191       {
3192          SCIP_Bool subinfeas = FALSE;
3193          SCIP_Bool convexsub;
3194          SCIP_Bool solvesub = TRUE;
3195          SCIP_Bool solved;
3196 
3197          i = solveidx[j];
3198          convexsub = SCIPbendersGetSubproblemType(benders, i) == SCIP_BENDERSSUBTYPE_CONVEXCONT;;
3199 
3200          /* the subproblem is initially flagged as not solved for this solving loop */
3201          (*subprobsolved)[i] = FALSE;
3202 
3203          /* setting the subsystem status to UNKNOWN at the start of each solve loop */
3204          (*substatus)[i] = SCIP_BENDERSSUBSTATUS_UNKNOWN;
3205 
3206          /* for the second solving loop, if the problem is an LP, it is not solved again. If the problem is a MIP,
3207           * then the subproblem objective function value is set to infinity. However, if the subproblem is proven
3208           * infeasible from the LP, then the IP loop is not performed.
3209           * If the solve loop is SCIP_BENDERSSOLVELOOP_USERCIP, then nothing is done. It is assumed that the user will
3210           * correctly update the objective function within the user-defined solving function.
3211           */
3212          if( solveloop == SCIP_BENDERSSOLVELOOP_CIP )
3213          {
3214             if( convexsub || (*substatus)[i] == SCIP_BENDERSSUBSTATUS_INFEAS )
3215                solvesub = FALSE;
3216             else
3217             {
3218                SCIPbendersSetSubproblemObjval(benders, i, SCIPbendersSubproblem(benders, i) != NULL ?
3219                   SCIPinfinity(SCIPbendersSubproblem(benders, i)) : SCIPsetInfinity(set));
3220             }
3221          }
3222 
3223          /* if the subproblem is independent, then it does not need to be solved. In this case, the nverified flag will
3224           * increase by one. When the subproblem is not independent, then it needs to be checked.
3225           */
3226          if( !subproblemIsActive(benders, i) )
3227          {
3228             /* NOTE: There is no need to update the optimal flag. This is because optimal is always TRUE until a
3229              * non-optimal subproblem is found.
3230              */
3231             /* if the auxiliary variable value is infinity, then the subproblem has not been solved yet. Currently the
3232              * subproblem statue is unknown. */
3233             if( SCIPsetIsInfinity(set, SCIPbendersGetAuxiliaryVarVal(benders, set, sol, i))
3234                || SCIPsetIsInfinity(set, -SCIPbendersGetAuxiliaryVarVal(benders, set, sol, i))
3235                || SCIPsetIsInfinity(set, -SCIPbendersGetSubproblemLowerbound(benders, i)) )
3236             {
3237                SCIPbendersSetSubproblemObjval(benders, i, SCIPbendersSubproblem(benders, i) != NULL ?
3238                   SCIPinfinity(SCIPbendersSubproblem(benders, i)) : SCIPsetInfinity(set));
3239 
3240                (*substatus)[i] = SCIP_BENDERSSUBSTATUS_UNKNOWN;
3241                locoptimal = FALSE;
3242 
3243                SCIPsetDebugMsg(set, "Benders' decomposition: subproblem %d is not active, but has not been solved."
3244                  " setting status to UNKNOWN\n", i);
3245             }
3246             else
3247             {
3248                if( SCIPrelDiff(SCIPbendersGetSubproblemLowerbound(benders, i),
3249                      SCIPbendersGetAuxiliaryVarVal(benders, set, sol, i)) < benders->solutiontol )
3250                {
3251                   SCIPbendersSetSubproblemObjval(benders, i, SCIPbendersGetAuxiliaryVarVal(benders, set, sol, i));
3252                   (*substatus)[i] = SCIP_BENDERSSUBSTATUS_OPTIMAL;
3253                }
3254                else
3255                {
3256                   SCIPbendersSetSubproblemObjval(benders, i, SCIPbendersGetSubproblemLowerbound(benders, i));
3257                   (*substatus)[i] = SCIP_BENDERSSUBSTATUS_AUXVIOL;
3258                }
3259 
3260                SCIPsetDebugMsg(set, "Benders' decomposition: subproblem %d is not active, setting status to OPTIMAL\n", i);
3261             }
3262 
3263             (*subprobsolved)[i] = TRUE;
3264 
3265             /* the nverified counter is only increased in the convex solving loop */
3266             if( solveloop == SCIP_BENDERSSOLVELOOP_CONVEX || solveloop == SCIP_BENDERSSOLVELOOP_USERCONVEX )
3267                locnverified++;
3268          }
3269          else if( solvesub )
3270          {
3271             retcode = SCIPbendersExecSubproblemSolve(benders, set, sol, i, solveloop, FALSE, &solved, &subinfeas, type);
3272 
3273             /* the solution for the subproblem is only processed if the return code is SCIP_OKAY */
3274             if( retcode == SCIP_OKAY )
3275             {
3276 #ifdef SCIP_DEBUG
3277                if( type == SCIP_BENDERSENFOTYPE_LP )
3278                {
3279                SCIPsetDebugMsg(set, "Enfo LP: Subproblem %d Type %d (%f < %f)\n", i,
3280                   SCIPbendersGetSubproblemType(benders, i), SCIPbendersGetAuxiliaryVarVal(benders, set, sol, i),
3281                      SCIPbendersGetSubproblemObjval(benders, i));
3282                }
3283 #endif
3284                (*subprobsolved)[i] = solved;
3285 
3286                locinfeasible = locinfeasible || subinfeas;
3287                if( subinfeas )
3288                   (*substatus)[i] = SCIP_BENDERSSUBSTATUS_INFEAS;
3289 
3290                /* if the subproblems are solved to check integer feasibility, then the optimality check must be performed.
3291                 * This will only be performed if checkint is TRUE and the subproblem was solved. The subproblem may not be
3292                 * solved if the user has defined a solving function
3293                 */
3294                if( checkint && (*subprobsolved)[i] )
3295                {
3296                   /* if the subproblem is feasible, then it is necessary to update the value of the auxiliary variable to the
3297                    * objective function value of the subproblem.
3298                    */
3299                   if( !subinfeas )
3300                   {
3301                      SCIP_Bool subproboptimal;
3302 
3303                      subproboptimal = SCIPbendersSubproblemIsOptimal(benders, set, sol, i);
3304 
3305                      if( subproboptimal )
3306                         (*substatus)[i] = SCIP_BENDERSSUBSTATUS_OPTIMAL;
3307                      else
3308                         (*substatus)[i] = SCIP_BENDERSSUBSTATUS_AUXVIOL;
3309 
3310                      /* It is only possible to determine the optimality of a solution within a given subproblem in four
3311                       * different cases:
3312                       * i) solveloop == SCIP_BENDERSSOLVELOOP_CONVEX or USERCONVEX and the subproblem is convex.
3313                       * ii) solveloop == SCIP_BENDERSOLVELOOP_CONVEX  and only the convex relaxations will be checked.
3314                       * iii) solveloop == SCIP_BENDERSSOLVELOOP_USERCIP and the subproblem was solved, since the user has
3315                       * defined a solve function, it is expected that the solving is correctly executed.
3316                       * iv) solveloop == SCIP_BENDERSSOLVELOOP_CIP and the MIP for the subproblem has been solved.
3317                       */
3318                      if( convexsub || onlyconvexcheck
3319                         || solveloop == SCIP_BENDERSSOLVELOOP_CIP
3320                         || solveloop == SCIP_BENDERSSOLVELOOP_USERCIP )
3321                         locoptimal = locoptimal && subproboptimal;
3322 
3323 #ifdef SCIP_DEBUG
3324                      if( convexsub || solveloop >= SCIP_BENDERSSOLVELOOP_CIP )
3325                      {
3326                         if( subproboptimal )
3327                         {
3328                            SCIPsetDebugMsg(set, "Subproblem %d is Optimal (%f >= %f)\n", i,
3329                               SCIPbendersGetAuxiliaryVarVal(benders, set, sol, i), SCIPbendersGetSubproblemObjval(benders, i));
3330                         }
3331                         else
3332                         {
3333                            SCIPsetDebugMsg(set, "Subproblem %d is NOT Optimal (%f < %f)\n", i,
3334                               SCIPbendersGetAuxiliaryVarVal(benders, set, sol, i), SCIPbendersGetSubproblemObjval(benders, i));
3335                         }
3336                      }
3337 #endif
3338 
3339                      /* the nverified variable is only incremented when the original form of the subproblem has been solved.
3340                       * What is meant by "original" is that the LP relaxation of CIPs are solved to generate valid cuts. So
3341                       * if the subproblem is defined as a CIP, then it is only classified as checked if the CIP is solved.
3342                       * There are three cases where the "original" form is solved are:
3343                       * i) solveloop == SCIP_BENDERSSOLVELOOP_CONVEX or USERCONVEX and the subproblem is an LP
3344                       *    - the original form has been solved.
3345                       * ii) solveloop == SCIP_BENDERSSOLVELOOP_CIP or USERCIP and the CIP for the subproblem has been
3346                       *    solved.
3347                       * iii) or, only a convex check is performed.
3348                       */
3349                      if( ((solveloop == SCIP_BENDERSSOLVELOOP_CONVEX || solveloop == SCIP_BENDERSSOLVELOOP_USERCONVEX)
3350                            && convexsub)
3351                         || ((solveloop == SCIP_BENDERSSOLVELOOP_CIP || solveloop == SCIP_BENDERSSOLVELOOP_USERCIP)
3352                            && !convexsub)
3353                         || onlyconvexcheck )
3354                         locnverified++;
3355                   }
3356                }
3357             }
3358          }
3359 
3360          /* checking whether the limits have been exceeded in the master problem */
3361          locstopped = SCIPisStopped(set->scip);
3362       }
3363    }
3364 
3365    /* setting the input parameters to the local variables */
3366    SCIPsetDebugMsg(set, "Local variable values: nverified %d infeasible %d optimal %d stopped %d\n", locnverified,
3367       locinfeasible, locoptimal, locstopped);
3368    *nverified = locnverified;
3369    *infeasible = locinfeasible;
3370    *optimal = locoptimal;
3371    *stopped = locstopped;
3372 
3373    return retcode;
3374 }
3375 
3376 /** Calls the Benders' decompsition cuts for the given solve loop. There are four cases:
3377  *  i) solveloop == SCIP_BENDERSSOLVELOOP_CONVEX - only the LP Benders' cuts are called
3378  *  ii) solveloop == SCIP_BENDERSSOLVELOOP_CIP - only the CIP Benders' cuts are called
3379  *  iii) solveloop == SCIP_BENDERSSOLVELOOP_USERCONVEX - only the LP Benders' cuts are called
3380  *  iv) solveloop == SCIP_BENDERSSOLVELOOP_USERCIP - only the CIP Benders' cuts are called
3381  *
3382  *  The priority of the results are: SCIP_CONSADDED (SCIP_SEPARATED), SCIP_DIDNOTFIND, SCIP_FEASIBLE, SCIP_DIDNOTRUN. In
3383  *  this function, there are four levels of results that need to be assessed. These are:
3384  *  i) The result from the individual cut for the subproblem
3385  *  ii) The overall result for the subproblem from all cuts
3386  *  iii) the overall result for the solve loop from all cuts
3387  *  iv) the over all result from all solve loops.
3388  *  In each level, the priority of results must be adhered to.
3389  */
3390 static
generateBendersCuts(SCIP_BENDERS * benders,SCIP_SET * set,SCIP_SOL * sol,SCIP_RESULT * result,SCIP_BENDERSENFOTYPE type,SCIP_BENDERSSOLVELOOP solveloop,SCIP_Bool checkint,SCIP_Bool * subprobsolved,SCIP_BENDERSSUBSTATUS * substatus,int * solveidx,int nsolveidx,int ** mergecands,int * npriomergecands,int * nmergecands,int * nsolveloops)3391 SCIP_RETCODE generateBendersCuts(
3392    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
3393    SCIP_SET*             set,                /**< global SCIP settings */
3394    SCIP_SOL*             sol,                /**< primal CIP solution */
3395    SCIP_RESULT*          result,             /**< result of the pricing process */
3396    SCIP_BENDERSENFOTYPE  type,               /**< the type of solution being enforced */
3397    SCIP_BENDERSSOLVELOOP solveloop,          /**< the current solve loop */
3398    SCIP_Bool             checkint,           /**< are the subproblems called during a check/enforce of integer sols? */
3399    SCIP_Bool*            subprobsolved,      /**< an array indicating the subproblems that were solved in this loop. */
3400    SCIP_BENDERSSUBSTATUS* substatus,         /**< array to store the status of the subsystem */
3401    int*                  solveidx,           /**< the indices of subproblems to be solved in this loop */
3402    int                   nsolveidx,          /**< the number of subproblems to be solved in this loop */
3403    int**                 mergecands,         /**< the subproblems that are merge candidates */
3404    int*                  npriomergecands,    /**< the number of priority merge candidates. */
3405    int*                  nmergecands,        /**< the number of merge candidates. */
3406    int*                  nsolveloops         /**< the number of solve loops, is updated w.r.t added cuts */
3407    )
3408 {
3409    SCIP_BENDERSCUT** benderscuts;
3410    SCIP_RESULT solveloopresult;
3411    int nbenderscuts;
3412    SCIP_Longint addedcuts = 0;
3413    int i;
3414    int j;
3415    int k;
3416    SCIP_Bool onlyconvexcheck;
3417 
3418    assert(benders != NULL);
3419    assert(set != NULL);
3420 
3421    /* getting the Benders' decomposition cuts */
3422    benderscuts = SCIPbendersGetBenderscuts(benders);
3423    nbenderscuts = SCIPbendersGetNBenderscuts(benders);
3424 
3425    solveloopresult = SCIP_DIDNOTRUN;
3426 
3427    /* in the case of an LNS check, only the convex relaxations of the subproblems will be solved. This is a performance
3428     * feature, since solving the convex relaxation is typically much faster than solving the corresponding CIP. While
3429     * the CIP is not solved during the LNS check, the solutions are still of higher quality than when Benders' is not
3430     * employed.
3431     */
3432    onlyconvexcheck = SCIPbendersOnlyCheckConvexRelax(benders, SCIPsetGetSubscipsOff(set));
3433 
3434    /* It is only possible to add cuts to the problem if it has not already been solved */
3435    if( SCIPsetGetStage(set) < SCIP_STAGE_SOLVED
3436       && SCIPsetGetStage(set) != SCIP_STAGE_TRANSFORMED && SCIPsetGetStage(set) != SCIP_STAGE_PRESOLVED
3437       && (benders->cutcheck || type != SCIP_BENDERSENFOTYPE_CHECK) )
3438    {
3439       /* This is done in two loops. The first is by subproblem and the second is by cut type. */
3440       for( k = 0; k < nsolveidx; k++ )
3441       {
3442          SCIP_RESULT subprobresult;
3443          SCIP_Bool convexsub;
3444 
3445          i = solveidx[k];
3446 
3447          convexsub = SCIPbendersGetSubproblemType(benders, i) == SCIP_BENDERSSUBTYPE_CONVEXCONT;
3448 
3449          /* cuts can only be generated if the subproblem is not independent and if it has been solved. Additionally, the
3450           * status of the subproblem solving must not be INFEASIBLE while in a cut strengthening round.
3451           * The subproblem solved flag is important for the user-defined subproblem solving methods
3452           */
3453          if( subproblemIsActive(benders, i) && subprobsolved[i]
3454             && !(substatus[i] == SCIP_BENDERSSUBSTATUS_INFEAS && benders->strengthenround) )
3455          {
3456             subprobresult = SCIP_DIDNOTRUN;
3457             for( j = 0; j < nbenderscuts; j++ )
3458             {
3459                SCIP_RESULT cutresult;
3460                SCIP_Longint prevaddedcuts;
3461 
3462                assert(benderscuts[j] != NULL);
3463 
3464                prevaddedcuts = SCIPbenderscutGetNFound(benderscuts[j]);
3465                cutresult = SCIP_DIDNOTRUN;
3466 
3467                /* the result is updated only if a Benders' cut is generated or one was not found. However, if a cut has
3468                 * been found in a previous iteration, then the result is returned as SCIP_CONSADDED or SCIP_SEPARATED.
3469                 * This result is permitted because if a constraint was added, the solution that caused the error in the cut
3470                 * generation will be cutoff from the master problem.
3471                 */
3472                if( (SCIPbenderscutIsLPCut(benderscuts[j]) && (solveloop == SCIP_BENDERSSOLVELOOP_CONVEX
3473                         || solveloop == SCIP_BENDERSSOLVELOOP_USERCONVEX))
3474                   || (!SCIPbenderscutIsLPCut(benderscuts[j]) && ((solveloop == SCIP_BENDERSSOLVELOOP_CIP && !convexsub)
3475                         || solveloop == SCIP_BENDERSSOLVELOOP_USERCIP)) )
3476                   SCIP_CALL( SCIPbenderscutExec(benderscuts[j], set, benders, sol, i, type, &cutresult) );
3477 
3478                addedcuts += (SCIPbenderscutGetNFound(benderscuts[j]) - prevaddedcuts);
3479 
3480                /* the result is updated only if a Benders' cut is generated */
3481                if( cutresult == SCIP_CONSADDED || cutresult == SCIP_SEPARATED )
3482                {
3483                   subprobresult = cutresult;
3484 
3485                   benders->ncutsfound++;
3486 
3487                   /* at most a single cut is generated for each subproblem */
3488                   break;
3489                }
3490                else
3491                {
3492                   /* checking from lowest priority result */
3493                   if( subprobresult == SCIP_DIDNOTRUN )
3494                      subprobresult = cutresult;
3495                   else if( subprobresult == SCIP_FEASIBLE && cutresult == SCIP_DIDNOTFIND )
3496                      subprobresult = cutresult;
3497                   /* if the subprobresult is SCIP_DIDNOTFIND, then it can't be updated. */
3498                }
3499             }
3500 
3501             /* the highest priority for the results is CONSADDED and SEPARATED. The solveloopresult will always be
3502              * updated if the subprobresult is either of these.
3503              */
3504             if( subprobresult == SCIP_CONSADDED || subprobresult == SCIP_SEPARATED )
3505             {
3506                solveloopresult = subprobresult;
3507             }
3508             else if( subprobresult == SCIP_FEASIBLE )
3509             {
3510                /* updating the solve loop result based upon the priority */
3511                if( solveloopresult == SCIP_DIDNOTRUN )
3512                   solveloopresult = subprobresult;
3513             }
3514             else if( subprobresult == SCIP_DIDNOTFIND )
3515             {
3516                /* updating the solve loop result based upon the priority */
3517                if( solveloopresult == SCIP_DIDNOTRUN || solveloopresult == SCIP_FEASIBLE )
3518                   solveloopresult = subprobresult;
3519 
3520                /* since a cut was not found, then merging could be useful to avoid this in subsequent iterations. The
3521                 * candidate is labelled as a non-priority merge candidate
3522                 */
3523                if( substatus[i] != SCIP_BENDERSSUBSTATUS_OPTIMAL )
3524                {
3525                   (*mergecands)[(*nmergecands)] = i;
3526                   (*nmergecands)++;
3527                }
3528             }
3529             else if( subprobresult == SCIP_DIDNOTRUN )
3530             {
3531                /* if the subproblem is infeasible and no cut generation methods were run, then the infeasibility will
3532                 * never be resolved. As such, the subproblem will be merged into the master problem. If the subproblem
3533                 * was not infeasible, then it is added as a possible merge candidate
3534                 */
3535                if( substatus[i] == SCIP_BENDERSSUBSTATUS_INFEAS )
3536                {
3537                   (*mergecands)[(*nmergecands)] = (*mergecands)[(*npriomergecands)];
3538                   (*mergecands)[(*npriomergecands)] = i;
3539                   (*npriomergecands)++;
3540                   (*nmergecands)++;
3541                }
3542                else if( substatus[i] != SCIP_BENDERSSUBSTATUS_OPTIMAL )
3543                {
3544                   (*mergecands)[(*nmergecands)] = i;
3545                   (*nmergecands)++;
3546                }
3547             }
3548          }
3549       }
3550    }
3551 
3552    /* updating the overall result based upon the priorities */
3553    if( solveloopresult == SCIP_CONSADDED || solveloopresult == SCIP_SEPARATED )
3554    {
3555       (*result) = solveloopresult;
3556    }
3557    else if( solveloopresult == SCIP_FEASIBLE )
3558    {
3559       /* updating the solve loop result based upon the priority */
3560       if( (*result) == SCIP_DIDNOTRUN )
3561          (*result) = solveloopresult;
3562    }
3563    else if( solveloopresult == SCIP_DIDNOTFIND )
3564    {
3565       /* updating the solve loop result based upon the priority */
3566       if( (*result) == SCIP_DIDNOTRUN || (*result) == SCIP_FEASIBLE )
3567          (*result) = solveloopresult;
3568    }
3569 
3570    /* if no cuts were added, then the number of solve loops is increased */
3571    if( addedcuts == 0 && SCIPbendersGetNConvexSubproblems(benders) < SCIPbendersGetNSubproblems(benders)
3572       && checkint && !onlyconvexcheck )
3573       (*nsolveloops) = 2;
3574 
3575    return SCIP_OKAY;
3576 }
3577 
3578 /** Solves the subproblem using the current master problem solution.
3579  *
3580  *  The checkint flag indicates whether integer feasibility can be assumed. If it is not assumed, i.e. checkint ==
3581  *  FALSE, then only the convex relaxations of the subproblems are solved. If integer feasibility is assumed, i.e.
3582  *  checkint == TRUE, then the convex relaxations and the full CIP are solved to generate Benders' cuts and check
3583  *  solution feasibility.
3584  *
3585  *  TODO: consider allowing the possibility to pass solution information back from the subproblems instead of the scip
3586  *  instance. This would allow the use of different solvers for the subproblems, more importantly allowing the use of an
3587  *  LP solver for LP subproblems.
3588  */
SCIPbendersExec(SCIP_BENDERS * benders,SCIP_SET * set,SCIP_SOL * sol,SCIP_RESULT * result,SCIP_Bool * infeasible,SCIP_Bool * auxviol,SCIP_BENDERSENFOTYPE type,SCIP_Bool checkint)3589 SCIP_RETCODE SCIPbendersExec(
3590    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
3591    SCIP_SET*             set,                /**< global SCIP settings */
3592    SCIP_SOL*             sol,                /**< primal CIP solution */
3593    SCIP_RESULT*          result,             /**< result of the pricing process */
3594    SCIP_Bool*            infeasible,         /**< is the master problem infeasible with respect to the Benders' cuts? */
3595    SCIP_Bool*            auxviol,            /**< set to TRUE only if the solution is feasible but the aux vars are violated */
3596    SCIP_BENDERSENFOTYPE  type,               /**< the type of solution being enforced */
3597    SCIP_Bool             checkint            /**< should the integer solution be checked by the subproblems */
3598    )
3599 {
3600    int nsubproblems;
3601    int subproblemcount;
3602    int nsolveloops;
3603    int nverified;
3604    int nsolved;
3605    int* mergecands;
3606    int npriomergecands;
3607    int nmergecands;
3608    int* solveidx;
3609    int* executedidx;
3610    int nsolveidx;
3611    int nexecutedidx;
3612    int nfree;
3613    SCIP_Bool* subprobsolved;
3614    SCIP_BENDERSSUBSTATUS* substatus;
3615    SCIP_Bool optimal;
3616    SCIP_Bool allverified;
3617    SCIP_Bool success;
3618    SCIP_Bool stopped;
3619    int i;
3620    int l;
3621 
3622    success = TRUE;
3623    stopped = FALSE;
3624 
3625    SCIPsetDebugMsg(set, "Starting Benders' decomposition subproblem solving. type %d checkint %d\n", type, checkint);
3626 
3627 #ifdef SCIP_MOREDEBUG
3628    SCIP_CALL( SCIPprintSol(set->scip, sol, NULL, FALSE) );
3629 #endif
3630 
3631    /* start timing */
3632    SCIPclockStart(benders->bendersclock, set);
3633 
3634    nsubproblems = SCIPbendersGetNSubproblems(benders);
3635 
3636    (*auxviol) = FALSE;
3637    (*infeasible) = FALSE;
3638 
3639    /* It is assumed that the problem is optimal, until a subproblem is found not to be optimal. However, not all
3640     * subproblems could be checked in each iteration. As such, it is not possible to state that the problem is optimal
3641     * if not all subproblems are checked. Situations where this may occur is when a subproblem is a MIP and only the LP
3642     * is solved. Also, in a distributed computation, then it may be advantageous to only solve some subproblems before
3643     * resolving the master problem. As such, for a problem to be optimal, then (optimal && allverified) == TRUE
3644     */
3645    optimal = TRUE;
3646    nverified = 0;
3647    nsolved = 0;
3648 
3649    assert(benders != NULL);
3650    assert(result != NULL);
3651    assert(infeasible != NULL);
3652    assert(auxviol != NULL);
3653 
3654    /* if the Benders' decomposition is called from a sub-SCIP and the sub-SCIPs have been deactivated, then it is
3655     * assumed that this is an LNS heuristic. As such, the check is not performed and the solution is assumed to be
3656     * feasible
3657     */
3658    if( benders->iscopy && set->subscipsoff
3659       && (!benders->lnscheck
3660          || (benders->lnsmaxdepth > -1 && SCIPgetDepth(benders->sourcescip) >= benders->lnsmaxdepth)
3661          || (benders->lnsmaxcalls > -1 && SCIPbendersGetNCalls(benders) >= benders->lnsmaxcalls)
3662          || (type != SCIP_BENDERSENFOTYPE_CHECK && SCIPgetDepth(set->scip) == 0 && benders->lnsmaxcallsroot > -1
3663             && SCIPbendersGetNCalls(benders) >= benders->lnsmaxcallsroot)) )
3664    {
3665       (*result) = SCIP_DIDNOTRUN;
3666       return SCIP_OKAY;
3667    }
3668 
3669    /* it is not necessary to check all primal solutions by solving the Benders' decomposition subproblems.
3670     * Only the improving solutions are checked to improve efficiency of the algorithm.
3671     * If the solution is non-improving, the result FEASIBLE is returned. While this may be incorrect w.r.t to the
3672     * Benders' subproblems, this solution will never be the optimal solution. A non-improving solution may be used
3673     * within LNS primal heuristics. If this occurs, the improving solution, if found, will be checked by the solving
3674     * the Benders' decomposition subproblems.
3675     * TODO: Add a parameter to control this behaviour.
3676     */
3677    if( checkint && SCIPsetIsLE(set, SCIPgetPrimalbound(set->scip)*(int)SCIPgetObjsense(set->scip),
3678          SCIPgetSolOrigObj(set->scip, sol)*(int)SCIPgetObjsense(set->scip)) )
3679    {
3680       (*result) = SCIP_DIDNOTRUN;
3681       return SCIP_OKAY;
3682    }
3683 
3684    /* if the enforcement type is SCIP_BENDERSENFOTYPE_LP and the LP is currently unbounded. This could mean that there
3685     * is no lower bound on the auxiliary variables. In this case, we try to update the lower bound for the auxiliary
3686     * variables.
3687     */
3688    if( type == SCIP_BENDERSENFOTYPE_LP && SCIPgetLPSolstat(set->scip) == SCIP_LPSOLSTAT_UNBOUNDEDRAY
3689       && benders->updateauxvarbound )
3690    {
3691       SCIP_CALL( updateAuxiliaryVarLowerbound(benders, set, result) );
3692 
3693       /* the auxiliary variable bound will only be updated once. */
3694       benders->updateauxvarbound = FALSE;
3695    }
3696 
3697    /* sets the stored objective function values of the subproblems to infinity */
3698    resetSubproblemObjectiveValue(benders, set);
3699 
3700    *result = SCIP_DIDNOTRUN;
3701 
3702    if( benders->benderspresubsolve != NULL && !benders->strengthenround )
3703    {
3704       SCIP_Bool skipsolve;
3705 
3706       skipsolve = FALSE;
3707       SCIP_CALL( benders->benderspresubsolve(set->scip, benders, sol, type, checkint, infeasible, auxviol, &skipsolve,
3708             result) );
3709 
3710       /* evaluate result */
3711       if( (*result) != SCIP_DIDNOTRUN
3712          && (*result) != SCIP_FEASIBLE
3713          && (*result) != SCIP_INFEASIBLE
3714          && (*result) != SCIP_CONSADDED
3715          && (*result) != SCIP_SEPARATED )
3716       {
3717          SCIPerrorMessage("the user-defined pre subproblem solving method for the Benders' decomposition <%s> returned "
3718             "invalid result <%d>\n", benders->name, *result);
3719          return SCIP_INVALIDRESULT;
3720       }
3721 
3722       /* if the solve must be skipped, then the solving loop is exited and the user defined result is returned */
3723       if( skipsolve )
3724       {
3725          SCIPsetDebugMsg(set, "skipping the subproblem solving for Benders' decomposition <%s>. "
3726             "returning result <%d>\n", benders->name, *result);
3727          return SCIP_OKAY;
3728       }
3729    }
3730 
3731    /* the cut strengthening is performed before the regular subproblem solve is called. To avoid recursion, the flag
3732     * strengthenround is set to TRUE when the cut strengthening is performed. The cut strengthening is not performed as
3733     * part of the large neighbourhood Benders' search.
3734     *
3735     * NOTE: cut strengthening is only applied for fractional solutions and integer solutions if there are no CIP
3736     * subproblems.
3737     */
3738    if( benders->strengthenenabled && !benders->strengthenround && !benders->iscopy
3739       && (!checkint || SCIPbendersGetNConvexSubproblems(benders) == SCIPbendersGetNSubproblems(benders)) )
3740    {
3741       SCIP_Bool skipsolve;
3742 
3743       benders->strengthenround = TRUE;
3744       /* if the user has not requested the solve to be skipped, then the cut strengthening is performed */
3745       SCIP_CALL( performInteriorSolCutStrengthening(benders, set, sol, type, checkint, FALSE, infeasible, auxviol,
3746             &skipsolve, result) );
3747       benders->strengthenround = FALSE;
3748 
3749       /* if the solve must be skipped, then the solving loop is exited and the user defined result is returned */
3750       if( skipsolve )
3751       {
3752          SCIPsetDebugMsg(set, "skipping the subproblem solving because cut strengthening found a cut "
3753             "for Benders' decomposition <%s>. Returning result <%d>\n", benders->name, *result);
3754          return SCIP_OKAY;
3755       }
3756 
3757       /* the result flag need to be reset to DIDNOTRUN for the main subproblem solve */
3758       (*result) = SCIP_DIDNOTRUN;
3759    }
3760 
3761    /* allocating memory for the infeasible subproblem array */
3762    SCIP_CALL( SCIPallocClearBlockMemoryArray(set->scip, &subprobsolved, nsubproblems) );
3763    SCIP_CALL( SCIPallocClearBlockMemoryArray(set->scip, &substatus, nsubproblems) );
3764    SCIP_CALL( SCIPallocClearBlockMemoryArray(set->scip, &mergecands, nsubproblems) );
3765    npriomergecands = 0;
3766    nmergecands = 0;
3767 
3768    /* allocating the memory for the subproblem solving and cut generation indices */
3769    SCIP_CALL( SCIPallocClearBlockMemoryArray(set->scip, &solveidx, nsubproblems) );
3770    SCIP_CALL( SCIPallocClearBlockMemoryArray(set->scip, &executedidx, nsubproblems) );
3771    nsolveidx = 0;
3772    nexecutedidx = 0;
3773 
3774    /* only a subset of the subproblems are initially solved. Both solving loops are executed for the subproblems to
3775     * check whether any cuts are generated. If a cut is generated, then no further subproblems are solved. If a cut is
3776     * not generated, then an additional set of subproblems are solved.
3777     */
3778    while( nsolved < nsubproblems )
3779    {
3780       /* getting the indices for the subproblems that will be solved */
3781       createSolveSubproblemIndexList(benders, set, type, &solveidx, &nsolveidx);
3782 
3783       /* by default the number of solve loops is 1. This is the case if all subproblems are LP or the user has defined a
3784        * benderssolvesub callback. If there is a subproblem that is not an LP, then 2 solve loops are performed. The first
3785        * loop is the LP solving loop, the second solves the subproblem to integer optimality.
3786        */
3787       nsolveloops = 1;
3788 
3789       for( l = 0; l < nsolveloops; l++ )
3790       {
3791          SCIP_BENDERSSOLVELOOP solveloop;    /* identifies what problem type is solve in this solve loop */
3792 
3793          /* if either benderssolvesubconvex or benderssolvesub are implemented, then the user callbacks are invoked */
3794          if( benders->benderssolvesubconvex != NULL || benders->benderssolvesub != NULL )
3795          {
3796             if( l == 0 )
3797                solveloop = SCIP_BENDERSSOLVELOOP_USERCONVEX;
3798             else
3799                solveloop = SCIP_BENDERSSOLVELOOP_USERCIP;
3800          }
3801          else
3802             solveloop = (SCIP_BENDERSSOLVELOOP) l;
3803 
3804          /* solving the subproblems for this round of enforcement/checking. */
3805          SCIP_CALL( solveBendersSubproblems(benders, set, sol, type, solveloop, checkint, &nverified,
3806                solveidx, nsolveidx, &subprobsolved, &substatus, infeasible, &optimal, &stopped) );
3807 
3808          /* if the solving has been stopped, then the subproblem solving and cut generation must terminate */
3809          if( stopped )
3810             break;
3811 
3812          /* Generating cuts for the subproblems. Cuts are only generated when the solution is from primal heuristics,
3813           * relaxations or the LP
3814           */
3815          if( type != SCIP_BENDERSENFOTYPE_PSEUDO )
3816          {
3817             SCIP_CALL( generateBendersCuts(benders, set, sol, result, type, solveloop, checkint, subprobsolved,
3818                   substatus, solveidx, nsolveidx, &mergecands, &npriomergecands, &nmergecands, &nsolveloops) );
3819          }
3820          else
3821          {
3822             /* The first solving loop solves the convex subproblems and the convex relaxations of the CIP subproblems. The
3823              * second solving loop solves the CIP subproblems. The second solving loop is only called if the integer
3824              * feasibility is being checked and if the convex subproblems and convex relaxations are not infeasible.
3825              */
3826             if( !(*infeasible) && checkint && !SCIPbendersOnlyCheckConvexRelax(benders, SCIPsetGetSubscipsOff(set))
3827                && SCIPbendersGetNConvexSubproblems(benders) < SCIPbendersGetNSubproblems(benders))
3828                nsolveloops = 2;
3829          }
3830       }
3831 
3832       nsolved += nsolveidx;
3833 
3834       /* storing the indices of the subproblems for which the solving loop was executed */
3835       for( i = 0; i < nsolveidx; i++ )
3836          executedidx[nexecutedidx++] = solveidx[i];
3837 
3838       /* if the result is CONSADDED or SEPARATED, then a cut is generated and no further subproblem processing is
3839        * required
3840        */
3841       if( (*result) == SCIP_CONSADDED || (*result) == SCIP_SEPARATED )
3842          break;
3843    }
3844 
3845    /* inserting the subproblems into the priority queue for the next solve call */
3846    SCIP_CALL( updateSubproblemStatQueue(benders, executedidx, nexecutedidx, TRUE) );
3847 
3848    if( stopped )
3849       goto TERMINATE;
3850 
3851    allverified = (nverified == nsubproblems);
3852 
3853    SCIPsetDebugMsg(set, "End Benders' decomposition subproblem solve. result %d infeasible %d auxviol %d nverified %d\n",
3854       *result, *infeasible, *auxviol, nverified);
3855 
3856 #ifdef SCIP_DEBUG
3857    if( (*result) == SCIP_CONSADDED )
3858    {
3859       SCIPsetDebugMsg(set, "Benders' decomposition: Cut added\n");
3860    }
3861 #endif
3862 
3863    /* if the number of checked pseudo solutions exceeds a set limit, then all subproblems are passed as merge
3864     * candidates. Currently, merging subproblems into the master problem is the only method for resolving numerical
3865     * troubles.
3866     *
3867     * We are only interested in the pseudo solutions that have been checked completely for integrality. This is
3868     * identified by checkint == TRUE. This means that the Benders' decomposition constraint is one of the last
3869     * constraint handlers that must resolve the infeasibility. If the Benders' decomposition framework can't resolve the
3870     * infeasibility, then this will result in an error.
3871     */
3872    if( type == SCIP_BENDERSENFOTYPE_PSEUDO && checkint )
3873    {
3874       benders->npseudosols++;
3875 
3876       if( benders->npseudosols > BENDERS_MAXPSEUDOSOLS )
3877       {
3878          /* if a priority merge candidate already exists, then no other merge candidates need to be added.*/
3879          if( npriomergecands == 0 )
3880          {
3881             /* all subproblems are added to the merge candidate list. The first active subproblem is added as a
3882              * priority merge candidate
3883              */
3884             nmergecands = 0;
3885             npriomergecands = 1;
3886             for( i = 0; i < nsubproblems; i++ )
3887             {
3888                /* only active subproblems are added to the merge candidate list */
3889                if( subproblemIsActive(benders, i) )
3890                {
3891                   mergecands[nmergecands] = i;
3892                   nmergecands++;
3893                }
3894             }
3895 
3896             SCIPverbMessage(set->scip, SCIP_VERBLEVEL_HIGH, NULL, "   The number of checked pseudo solutions exceeds the "
3897               "limit of %d. All active subproblems are merge candidates, with subproblem %d a priority candidate.\n",
3898               BENDERS_MAXPSEUDOSOLS, mergecands[0]);
3899          }
3900       }
3901    }
3902    else
3903       benders->npseudosols = 0;
3904 
3905    /* if the result is SCIP_DIDNOTFIND, then there was a error in generating cuts in all subproblems that are not
3906     * optimal. This result does not cutoff any solution, so the Benders' decomposition algorithm will fail.
3907     *
3908     * It could happen that the cut strengthening approach causes an error the cut generation. In this case, an error
3909     * should not be thrown. So, this check will be skipped when in a strengthening round.
3910     * TODO: Work out a way to ensure Benders' decomposition does not terminate due to a SCIP_DIDNOTFIND result.
3911     */
3912    if( (*result) == SCIP_DIDNOTFIND && !benders->strengthenround )
3913    {
3914       if( type == SCIP_BENDERSENFOTYPE_PSEUDO )
3915          (*result) = SCIP_SOLVELP;
3916       else
3917          (*result) = SCIP_INFEASIBLE;
3918 
3919       SCIPerrorMessage("An error was found when generating cuts for non-optimal subproblems of Benders' "
3920          "decomposition <%s>. Consider merging the infeasible subproblems into the master problem.\n", SCIPbendersGetName(benders));
3921 
3922       /* since no other cuts are generated, then this error will result in a crash. It is possible to avoid the error,
3923        * by merging the affected subproblem into the master problem.
3924        *
3925        * NOTE: If the error occurs while checking solutions, i.e. SCIP_BENDERSENFOTYPE_CHECK, then it is valid to set
3926        * the result to SCIP_INFEASIBLE and the success flag to TRUE
3927        */
3928       if( type != SCIP_BENDERSENFOTYPE_CHECK )
3929          success = FALSE;
3930 
3931       goto POSTSOLVE;
3932    }
3933 
3934    if( type == SCIP_BENDERSENFOTYPE_PSEUDO )
3935    {
3936       if( (*infeasible) || !allverified )
3937          (*result) = SCIP_SOLVELP;
3938       else
3939       {
3940          (*result) = SCIP_FEASIBLE;
3941 
3942          /* if the subproblems are not infeasible, but they are also not optimal. This means that there is a violation
3943           * in the auxiliary variable values. In this case, a feasible result is returned with the auxviol flag set to
3944           * TRUE.
3945           */
3946          (*auxviol) = !optimal;
3947       }
3948    }
3949    else if( checkint && (type == SCIP_BENDERSENFOTYPE_CHECK
3950          || ((*result) != SCIP_CONSADDED && (*result) != SCIP_SEPARATED)) )
3951    {
3952       /* if the subproblems are being solved as part of conscheck, then the results flag must be returned after the solving
3953        * has completed.
3954        */
3955       if( (*infeasible) || !allverified )
3956          (*result) = SCIP_INFEASIBLE;
3957       else
3958       {
3959          (*result) = SCIP_FEASIBLE;
3960 
3961          /* if the subproblems are not infeasible, but they are also not optimal. This means that there is a violation
3962           * in the auxiliary variable values. In this case, a feasible result is returned with the auxviol flag set to
3963           * TRUE.
3964           */
3965          (*auxviol) = !optimal;
3966       }
3967    }
3968 
3969 POSTSOLVE:
3970    /* calling the post-solve call back for the Benders' decomposition algorithm. This allows the user to work directly
3971     * with the solved subproblems and the master problem */
3972    if( benders->benderspostsolve != NULL )
3973    {
3974       SCIP_Bool merged;
3975 
3976       merged = FALSE;
3977 
3978       SCIP_CALL( benders->benderspostsolve(set->scip, benders, sol, type, mergecands, npriomergecands, nmergecands,
3979             checkint, (*infeasible), &merged) );
3980 
3981       if( merged )
3982       {
3983          (*result) = SCIP_CONSADDED;
3984 
3985          /* since subproblems have been merged, then constraints have been added. This could resolve the unresolved
3986           * infeasibility, so the error has been corrected.
3987           */
3988          success = TRUE;
3989       }
3990       else if( !success )
3991       {
3992          SCIPerrorMessage("An error occurred during Benders' decomposition cut generations and no merging had been "
3993             "performed. It is not possible to continue solving the problem by Benders' decomposition\n");
3994       }
3995    }
3996 
3997 TERMINATE:
3998    /* if the solving process has stopped, then all subproblems need to be freed */
3999    if( stopped )
4000       nfree = nsubproblems;
4001    else
4002       nfree = nexecutedidx;
4003 
4004    /* freeing the subproblems after the cuts are generated */
4005    subproblemcount = 0;
4006    while( subproblemcount < nfree )
4007    {
4008       int subidx;
4009 
4010       if( stopped )
4011          subidx = subproblemcount;
4012       else
4013          subidx = executedidx[subproblemcount];
4014 
4015       SCIP_CALL( SCIPbendersFreeSubproblem(benders, set, subidx) );
4016 
4017       subproblemcount++;
4018    }
4019 
4020 #ifndef NDEBUG
4021    for( i = 0; i < nsubproblems; i++ )
4022       assert(SCIPbendersSubproblem(benders, i) == NULL
4023          || SCIPgetStage(SCIPbendersSubproblem(benders, i)) < SCIP_STAGE_TRANSFORMED
4024          || !SCIPinProbing(SCIPbendersSubproblem(benders, i))
4025          || !subproblemIsActive(benders, i));
4026 #endif
4027 
4028    /* increment the number of calls to the Benders' decomposition subproblem solve */
4029    benders->ncalls++;
4030 
4031    SCIPsetDebugMsg(set, "End Benders' decomposition execution method. result %d infeasible %d auxviol %d\n", *result,
4032       *infeasible, *auxviol);
4033 
4034    /* end timing */
4035    SCIPclockStop(benders->bendersclock, set);
4036 
4037    /* freeing memory */
4038    SCIPfreeBlockMemoryArray(set->scip, &executedidx, nsubproblems);
4039    SCIPfreeBlockMemoryArray(set->scip, &solveidx, nsubproblems);
4040    SCIPfreeBlockMemoryArray(set->scip, &mergecands, nsubproblems);
4041    SCIPfreeBlockMemoryArray(set->scip, &substatus, nsubproblems);
4042    SCIPfreeBlockMemoryArray(set->scip, &subprobsolved, nsubproblems);
4043 
4044    /* if there was an error in generating cuts and merging was not performed, then the solution is perturbed in an
4045     * attempt to generate a cut and correct the infeasibility
4046     */
4047    if( !success && !stopped )
4048    {
4049       SCIP_Bool skipsolve;
4050       SCIP_RESULT perturbresult;
4051 
4052       skipsolve = FALSE;
4053 
4054       benders->strengthenround = TRUE;
4055       /* if the user has not requested the solve to be skipped, then the cut strengthening is performed */
4056       SCIP_CALL( performInteriorSolCutStrengthening(benders, set, sol, type, checkint, TRUE, infeasible, auxviol,
4057             &skipsolve, &perturbresult) );
4058       benders->strengthenround = FALSE;
4059 
4060       if( perturbresult == SCIP_CONSADDED || perturbresult == SCIP_SEPARATED )
4061          (*result) = perturbresult;
4062 
4063       success = skipsolve;
4064    }
4065 
4066    /* if the Benders' decomposition subproblem check stopped, then we don't have a valid result. In this case, the
4067     * safest thing to do is report INFEASIBLE.
4068     */
4069    if( stopped )
4070       (*result) = SCIP_INFEASIBLE;
4071 
4072    /* if the subproblem verification identifies the solution as feasible, then a check whether slack variables have been
4073     * used is necessary. If any slack variables are non-zero, then the solution is reverified after the objective
4074     * coefficient for the slack variables is increased.
4075     */
4076    if( (*result) == SCIP_FEASIBLE )
4077    {
4078       SCIP_Bool activeslack;
4079 
4080       SCIP_CALL( SCIPbendersSolSlackVarsActive(benders, &activeslack) );
4081       SCIPsetDebugMsg(set, "Type: %d Active slack: %d Feasibility Phase: %d\n", type, activeslack,
4082          benders->feasibilityphase);
4083       if( activeslack )
4084       {
4085          if( type == SCIP_BENDERSENFOTYPE_CHECK )
4086             (*result) = SCIP_INFEASIBLE;
4087          else
4088          {
4089             /* increasing the value of the slack variable by a factor of 10 */
4090             benders->slackvarcoef *= 10;
4091 
4092             printf("Increasing the slack variable coefficient to %g\n", benders->slackvarcoef);
4093 
4094             /* resolving the subproblems with an increased slack variable */
4095             SCIP_CALL( SCIPsolveBendersSubproblems(set->scip, benders, sol, result, infeasible, auxviol, type, checkint) );
4096          }
4097       }
4098       else if( benders->feasibilityphase )
4099       {
4100          if( type != SCIP_BENDERSENFOTYPE_CHECK )
4101          {
4102             /* disabling the feasibility phase */
4103             benders->feasibilityphase = FALSE;
4104 
4105             /* resolving the subproblems with the slack variables set to zero */
4106             SCIP_CALL( SCIPsolveBendersSubproblems(set->scip, benders, sol, result, infeasible, auxviol, type, checkint) );
4107          }
4108       }
4109    }
4110 
4111    if( !success )
4112       return SCIP_ERROR;
4113    else
4114       return SCIP_OKAY;
4115 }
4116 
4117 /** solves the user-defined subproblem solving function */
4118 static
executeUserDefinedSolvesub(SCIP_BENDERS * benders,SCIP_SET * set,SCIP_SOL * sol,int probnumber,SCIP_BENDERSSOLVELOOP solveloop,SCIP_Bool * infeasible,SCIP_Real * objective,SCIP_RESULT * result)4119 SCIP_RETCODE executeUserDefinedSolvesub(
4120    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
4121    SCIP_SET*             set,                /**< global SCIP settings */
4122    SCIP_SOL*             sol,                /**< primal CIP solution */
4123    int                   probnumber,         /**< the subproblem number */
4124    SCIP_BENDERSSOLVELOOP solveloop,          /**< the solve loop iteration. The first iter is for LP, the second for IP */
4125    SCIP_Bool*            infeasible,         /**< returns whether the current subproblem is infeasible */
4126    SCIP_Real*            objective,          /**< the objective function value of the subproblem */
4127    SCIP_RESULT*          result              /**< the result from solving the subproblem */
4128    )
4129 {
4130    assert(benders != NULL);
4131    assert(probnumber >= 0 && probnumber < benders->nsubproblems);
4132    assert(benders->benderssolvesubconvex != NULL || benders->benderssolvesub != NULL);
4133 
4134    assert(solveloop == SCIP_BENDERSSOLVELOOP_USERCONVEX || solveloop == SCIP_BENDERSSOLVELOOP_USERCIP);
4135 
4136    (*objective) = -SCIPsetInfinity(set);
4137 
4138    /* calls the user defined subproblem solving method. Only the convex relaxations are solved during the Large
4139     * Neighbourhood Benders' Search. */
4140    if( solveloop == SCIP_BENDERSSOLVELOOP_USERCONVEX )
4141    {
4142       if( benders->benderssolvesubconvex != NULL )
4143       {
4144          SCIP_CALL( benders->benderssolvesubconvex(set->scip, benders, sol, probnumber,
4145                SCIPbendersOnlyCheckConvexRelax(benders, SCIPsetGetSubscipsOff(set)), objective, result) );
4146       }
4147       else
4148          (*result) = SCIP_DIDNOTRUN;
4149    }
4150    else if( solveloop == SCIP_BENDERSSOLVELOOP_USERCIP )
4151    {
4152       if( benders->benderssolvesub != NULL )
4153       {
4154          SCIP_CALL( benders->benderssolvesub(set->scip, benders, sol, probnumber, objective, result) );
4155       }
4156       else
4157          (*result) = SCIP_DIDNOTRUN;
4158    }
4159 
4160    /* evaluate result */
4161    if( (*result) != SCIP_DIDNOTRUN
4162       && (*result) != SCIP_FEASIBLE
4163       && (*result) != SCIP_INFEASIBLE
4164       && (*result) != SCIP_UNBOUNDED )
4165    {
4166       SCIPerrorMessage("the user-defined solving method for the Benders' decomposition <%s> returned invalid result <%d>\n",
4167          benders->name, *result);
4168       return SCIP_INVALIDRESULT;
4169    }
4170 
4171    if( (*result) == SCIP_INFEASIBLE )
4172       (*infeasible) = TRUE;
4173 
4174    if( (*result) == SCIP_FEASIBLE
4175       && (SCIPsetIsInfinity(set, -(*objective)) || SCIPsetIsInfinity(set, (*objective))) )
4176    {
4177       SCIPerrorMessage("the user-defined solving method for the Benders' decomposition <%s> returned objective value %g\n",
4178          benders->name, (*objective));
4179       return SCIP_ERROR;
4180    }
4181 
4182    /* if the result is SCIP_DIDNOTFIND, then an error is returned and SCIP will terminate. */
4183    if( (*result) == SCIP_DIDNOTFIND )
4184       return SCIP_ERROR;
4185    else
4186       return SCIP_OKAY;
4187 }
4188 
4189 /** executes the subproblem solving process */
SCIPbendersExecSubproblemSolve(SCIP_BENDERS * benders,SCIP_SET * set,SCIP_SOL * sol,int probnumber,SCIP_BENDERSSOLVELOOP solveloop,SCIP_Bool enhancement,SCIP_Bool * solved,SCIP_Bool * infeasible,SCIP_BENDERSENFOTYPE type)4190 SCIP_RETCODE SCIPbendersExecSubproblemSolve(
4191    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
4192    SCIP_SET*             set,                /**< global SCIP settings */
4193    SCIP_SOL*             sol,                /**< primal CIP solution */
4194    int                   probnumber,         /**< the subproblem number */
4195    SCIP_BENDERSSOLVELOOP solveloop,          /**< the solve loop iteration. The first iter is for LP, the second for IP */
4196    SCIP_Bool             enhancement,        /**< is the solve performed as part of and enhancement? */
4197    SCIP_Bool*            solved,             /**< flag to indicate whether the subproblem was solved */
4198    SCIP_Bool*            infeasible,         /**< returns whether the current subproblem is infeasible */
4199    SCIP_BENDERSENFOTYPE  type                /**< the enforcement type calling this function */
4200    )
4201 {  /*lint --e{715}*/
4202    SCIP* subproblem;
4203    SCIP_RESULT result;
4204    SCIP_Real objective;
4205    SCIP_STATUS solvestatus = SCIP_STATUS_UNKNOWN;
4206 
4207    assert(benders != NULL);
4208    assert(probnumber >= 0 && probnumber < benders->nsubproblems);
4209 
4210    SCIPsetDebugMsg(set, "Benders' decomposition: solving subproblem %d\n", probnumber);
4211 
4212    result = SCIP_DIDNOTRUN;
4213    objective = SCIPsetInfinity(set);
4214 
4215    subproblem = SCIPbendersSubproblem(benders, probnumber);
4216 
4217    if( subproblem == NULL && (benders->benderssolvesubconvex == NULL || benders->benderssolvesub == NULL) )
4218    {
4219       SCIPerrorMessage("The subproblem %d is set to NULL, but both bendersSolvesubconvex%s and bendersSolvesub%s "
4220          "are not defined.\n", probnumber, benders->name, benders->name);
4221       SCIPABORT();
4222       return SCIP_ERROR;
4223    }
4224 
4225    /* initially setting the solved flag to FALSE */
4226    (*solved) = FALSE;
4227 
4228    /* if the subproblem solve callback is implemented, then that is used instead of the default setup */
4229    if( solveloop == SCIP_BENDERSSOLVELOOP_USERCONVEX || solveloop == SCIP_BENDERSSOLVELOOP_USERCIP )
4230    {
4231       /* calls the user defined subproblem solving method. Only the convex relaxations are solved during the Large
4232        * Neighbourhood Benders' Search. */
4233       SCIP_CALL( executeUserDefinedSolvesub(benders, set, sol, probnumber, solveloop, infeasible, &objective, &result) );
4234 
4235       /* if the result is DIDNOTRUN, then the subproblem was not solved */
4236       (*solved) = (result != SCIP_DIDNOTRUN);
4237    }
4238    else if( subproblem != NULL )
4239    {
4240       /* setting up the subproblem */
4241       if( solveloop == SCIP_BENDERSSOLVELOOP_CONVEX )
4242       {
4243          SCIP_CALL( SCIPbendersSetupSubproblem(benders, set, sol, probnumber, type) );
4244 
4245          /* if the limits of the master problem were hit during the setup process, then the subproblem will not have
4246           * been setup. In this case, the solving function must be exited.
4247           */
4248          if( !SCIPbendersSubproblemIsSetup(benders, probnumber) )
4249          {
4250             SCIPbendersSetSubproblemObjval(benders, probnumber, SCIPsetInfinity(set));
4251             (*solved) = FALSE;
4252             return SCIP_OKAY;
4253          }
4254       }
4255       else
4256       {
4257          SCIP_CALL( updateEventhdlrUpperbound(benders, probnumber, SCIPbendersGetAuxiliaryVarVal(benders, set, sol, probnumber)) );
4258       }
4259 
4260       /* solving the subproblem
4261        * the LP of the subproblem is solved in the first solveloop.
4262        * In the second solve loop, the MIP problem is solved */
4263       if( solveloop == SCIP_BENDERSSOLVELOOP_CONVEX
4264          || SCIPbendersGetSubproblemType(benders, probnumber) == SCIP_BENDERSSUBTYPE_CONVEXCONT )
4265       {
4266          SCIP_CALL( SCIPbendersSolveSubproblemLP(set->scip, benders, probnumber, &solvestatus, &objective) );
4267 
4268          /* if the (N)LP was solved without error, then the subproblem is labelled as solved */
4269          if( solvestatus == SCIP_STATUS_OPTIMAL || solvestatus == SCIP_STATUS_INFEASIBLE )
4270             (*solved) = TRUE;
4271 
4272          if( solvestatus == SCIP_STATUS_INFEASIBLE )
4273             (*infeasible) = TRUE;
4274       }
4275       else
4276       {
4277          SCIP_SOL* bestsol;
4278 
4279          SCIP_CALL( SCIPbendersSolveSubproblemCIP(set->scip, benders, probnumber, &solvestatus, FALSE) );
4280 
4281          if( solvestatus == SCIP_STATUS_INFEASIBLE )
4282             (*infeasible) = TRUE;
4283 
4284          /* if the generic subproblem solving methods are used, then the CIP subproblems are always solved. */
4285          (*solved) = TRUE;
4286 
4287          bestsol = SCIPgetBestSol(subproblem);
4288          if( bestsol != NULL )
4289             objective = SCIPgetSolOrigObj(subproblem, bestsol)*(int)SCIPgetObjsense(set->scip);
4290          else
4291             objective = SCIPsetInfinity(set);
4292       }
4293    }
4294    else
4295    {
4296       SCIPABORT();
4297    }
4298 
4299    if( !enhancement )
4300    {
4301       /* The following handles the cases when the subproblem is OPTIMAL, INFEASIBLE and UNBOUNDED.
4302        * If a subproblem is unbounded, then the auxiliary variables are set to -infinity and the unbounded flag is
4303        * returned as TRUE. No cut will be generated, but the result will be set to SCIP_FEASIBLE.
4304        */
4305       if( solveloop == SCIP_BENDERSSOLVELOOP_CONVEX || solveloop == SCIP_BENDERSSOLVELOOP_CIP )
4306       {
4307          /* TODO: Consider whether other solutions status should be handled */
4308          if( solvestatus == SCIP_STATUS_OPTIMAL )
4309             SCIPbendersSetSubproblemObjval(benders, probnumber, objective);
4310          else if( solvestatus == SCIP_STATUS_INFEASIBLE )
4311             SCIPbendersSetSubproblemObjval(benders, probnumber, SCIPsetInfinity(set));
4312          else if( solvestatus == SCIP_STATUS_USERINTERRUPT || solvestatus == SCIP_STATUS_BESTSOLLIMIT )
4313             SCIPbendersSetSubproblemObjval(benders, probnumber, objective);
4314          else if( solvestatus == SCIP_STATUS_MEMLIMIT || solvestatus == SCIP_STATUS_TIMELIMIT
4315             || solvestatus == SCIP_STATUS_UNKNOWN )
4316          {
4317             SCIPverbMessage(set->scip, SCIP_VERBLEVEL_FULL, NULL, "   Benders' decomposition: Error solving "
4318                "subproblem %d. No cut will be generated for this subproblem.\n", probnumber);
4319             SCIPbendersSetSubproblemObjval(benders, probnumber, SCIPsetInfinity(set));
4320          }
4321          else if( solvestatus == SCIP_STATUS_UNBOUNDED )
4322          {
4323             SCIPerrorMessage("The Benders' decomposition subproblem %d is unbounded. This should not happen.\n",
4324                probnumber);
4325             SCIPABORT();
4326          }
4327          else
4328          {
4329             SCIPerrorMessage("Invalid status returned from solving Benders' decomposition subproblem %d. Solution status: %d\n",
4330                probnumber, solvestatus);
4331             SCIPABORT();
4332          }
4333       }
4334       else
4335       {
4336          assert(solveloop == SCIP_BENDERSSOLVELOOP_USERCONVEX || solveloop == SCIP_BENDERSSOLVELOOP_USERCIP);
4337          if( result == SCIP_FEASIBLE )
4338             SCIPbendersSetSubproblemObjval(benders, probnumber, objective);
4339          else if( result == SCIP_INFEASIBLE )
4340             SCIPbendersSetSubproblemObjval(benders, probnumber, SCIPsetInfinity(set));
4341          else if( result == SCIP_UNBOUNDED )
4342          {
4343             SCIPerrorMessage("The Benders' decomposition subproblem %d is unbounded. This should not happen.\n",
4344                probnumber);
4345             SCIPABORT();
4346          }
4347          else if( result != SCIP_DIDNOTRUN )
4348          {
4349             SCIPerrorMessage("Invalid result <%d> from user-defined subproblem solving method. This should not happen.\n",
4350                result);
4351          }
4352       }
4353    }
4354 
4355    return SCIP_OKAY;
4356 }
4357 
4358 /** sets up the subproblem using the solution to the master problem  */
SCIPbendersSetupSubproblem(SCIP_BENDERS * benders,SCIP_SET * set,SCIP_SOL * sol,int probnumber,SCIP_BENDERSENFOTYPE type)4359 SCIP_RETCODE SCIPbendersSetupSubproblem(
4360    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
4361    SCIP_SET*             set,                /**< global SCIP settings */
4362    SCIP_SOL*             sol,                /**< primal CIP solution */
4363    int                   probnumber,         /**< the subproblem number */
4364    SCIP_BENDERSENFOTYPE  type                /**< the enforcement type calling this function */
4365    )
4366 {
4367    SCIP* subproblem;
4368    SCIP_VAR** vars;
4369    SCIP_VAR* mastervar;
4370    SCIP_Real solval;
4371    int nvars;
4372    int i;
4373 
4374    assert(benders != NULL);
4375    assert(set != NULL);
4376    assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders));
4377 
4378    subproblem = SCIPbendersSubproblem(benders, probnumber);
4379 
4380    /* the subproblem setup can only be performed if the subproblem is not NULL */
4381    if( subproblem == NULL )
4382    {
4383       SCIPerrorMessage("The subproblem %d is NULL. Thus, the subproblem setup must be performed manually in either "
4384          "bendersSolvesubconvex%s or bendersSolvesub%s.\n", probnumber, benders->name, benders->name);
4385       return SCIP_ERROR;
4386    }
4387    assert(subproblem != NULL);
4388 
4389    /* changing all of the master problem variable to continuous. */
4390    SCIP_CALL( SCIPbendersChgMastervarsToCont(benders, set, probnumber) );
4391 
4392    /* if the Benders' decomposition subproblem is convex and has continuous variables, then probing mode
4393     * must be started.
4394     * If the subproblem contains non-convex constraints or discrete variables, then the problem must be initialised,
4395     * and then put into SCIP_STAGE_SOLVING to be able to change the variable bounds. The probing mode is entered once
4396     * the variable bounds are set.
4397     * In the latter case, the transformed problem is freed after each subproblem solve round. */
4398    if( SCIPbendersGetSubproblemType(benders, probnumber) == SCIP_BENDERSSUBTYPE_CONVEXCONT )
4399    {
4400       SCIP_CALL( SCIPstartProbing(subproblem) );
4401    }
4402    else
4403    {
4404       SCIP_Bool success;
4405 
4406       SCIP_CALL( initialiseSubproblem(benders, set, probnumber, &success) );
4407 
4408       if( !success )
4409       {
4410          /* set the flag to indicate that the subproblems have been set up */
4411          SCIPbendersSetSubproblemIsSetup(benders, probnumber, FALSE);
4412 
4413          return SCIP_OKAY;
4414       }
4415    }
4416 
4417    vars = SCIPgetVars(subproblem);
4418    nvars = SCIPgetNVars(subproblem);
4419 
4420    /* looping over all variables in the subproblem to find those corresponding to the master problem variables. */
4421    /* TODO: It should be possible to store the pointers to the master variables to speed up the subproblem setup */
4422    for( i = 0; i < nvars; i++ )
4423    {
4424       SCIP_CALL( SCIPbendersGetVar(benders, set, vars[i], &mastervar, -1) );
4425 
4426       if( mastervar != NULL )
4427       {
4428          /* It is possible due to numerics that the solution value exceeds the upper or lower bounds. When this
4429           * happens, it causes an error in the LP solver as a result of inconsistent bounds. So the following statements
4430           * are used to ensure that the bounds are not exceeded when applying the fixings for the Benders'
4431           * decomposition subproblems
4432           */
4433          solval = SCIPgetSolVal(set->scip, sol, mastervar);
4434          if( !SCIPisLT(set->scip, solval, SCIPvarGetUbLocal(vars[i])) )
4435             solval = SCIPvarGetUbLocal(vars[i]);
4436          else if( !SCIPisGT(set->scip, solval, SCIPvarGetLbLocal(vars[i])) )
4437             solval = SCIPvarGetLbLocal(vars[i]);
4438 
4439          /* fixing the variable in the subproblem */
4440          if( !SCIPisEQ(subproblem, SCIPvarGetLbLocal(vars[i]), SCIPvarGetUbLocal(vars[i])) )
4441          {
4442             if( SCIPisGT(subproblem, solval, SCIPvarGetLbLocal(vars[i])) )
4443             {
4444                SCIP_CALL( SCIPchgVarLb(subproblem, vars[i], solval) );
4445             }
4446             if( SCIPisLT(subproblem, solval, SCIPvarGetUbLocal(vars[i])) )
4447             {
4448                SCIP_CALL( SCIPchgVarUb(subproblem, vars[i], solval) );
4449             }
4450          }
4451 
4452          assert(SCIPisEQ(subproblem, SCIPvarGetLbLocal(vars[i]), SCIPvarGetUbLocal(vars[i])));
4453       }
4454       else if( strstr(SCIPvarGetName(vars[i]), SLACKVAR_NAME) != NULL )
4455       {
4456          /* if the slack variables have been added to help improve feasibility, then they remain unfixed with a large
4457           * objective coefficient. Once the root node has been solved to optimality, then the slack variables are
4458           * fixed to zero.
4459           */
4460          if( benders->feasibilityphase && SCIPgetDepth(set->scip) == 0 && type != SCIP_BENDERSENFOTYPE_CHECK )
4461          {
4462             /* The coefficient update can only be performed if the subproblem is in probing mode. */
4463             if( SCIPinProbing(subproblem) )
4464             {
4465                SCIP_Real coef = benders->slackvarcoef;
4466 
4467                SCIP_CALL( SCIPchgVarObjProbing(subproblem, vars[i], coef) );
4468             }
4469          }
4470          else
4471          {
4472             /* if the subproblem is non-linear and convex, then slack variables have been added to the subproblem. These
4473              * need to be fixed to zero when first solving the subproblem. However, if the slack variables have been added
4474              * by setting the execfeasphase runtime parameter, then they must not get fixed to zero
4475              */
4476             assert( !SCIPisEQ(subproblem, SCIPvarGetLbLocal(vars[i]), SCIPvarGetUbLocal(vars[i])) );
4477             assert( SCIPisZero(subproblem, SCIPvarGetLbLocal(vars[i])) );
4478 
4479             if( SCIPisLT(subproblem, 0.0, SCIPvarGetUbLocal(vars[i])) )
4480             {
4481                SCIP_CALL( SCIPchgVarUb(subproblem, vars[i], 0.0) );
4482             }
4483          }
4484       }
4485    }
4486 
4487    /* if the subproblem contain non-convex constraints or discrete variables, then the probing mode is entered after
4488     * setting up the subproblem
4489     */
4490    if( SCIPbendersGetSubproblemType(benders, probnumber) != SCIP_BENDERSSUBTYPE_CONVEXCONT )
4491    {
4492       SCIP_CALL( SCIPstartProbing(subproblem) );
4493    }
4494 
4495    /* set the flag to indicate that the subproblems have been set up */
4496    SCIPbendersSetSubproblemIsSetup(benders, probnumber, TRUE);
4497 
4498    return SCIP_OKAY;
4499 }
4500 
4501 /** Solve a Benders' decomposition subproblems. This will either call the user defined method or the generic solving
4502  *  methods. If the generic method is called, then the subproblem must be set up before calling this method. */
SCIPbendersSolveSubproblem(SCIP_BENDERS * benders,SCIP_SET * set,SCIP_SOL * sol,int probnumber,SCIP_Bool * infeasible,SCIP_Bool solvecip,SCIP_Real * objective)4503 SCIP_RETCODE SCIPbendersSolveSubproblem(
4504    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
4505    SCIP_SET*             set,                /**< global SCIP settings */
4506    SCIP_SOL*             sol,                /**< primal CIP solution, can be NULL */
4507    int                   probnumber,         /**< the subproblem number */
4508    SCIP_Bool*            infeasible,         /**< returns whether the current subproblem is infeasible */
4509    SCIP_Bool             solvecip,           /**< directly solve the CIP subproblem */
4510    SCIP_Real*            objective           /**< the objective function value of the subproblem, can be NULL */
4511    )
4512 {
4513    assert(benders != NULL);
4514    assert(set != NULL);
4515    assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders));
4516 
4517    (*infeasible) = FALSE;
4518 
4519    /* the subproblem must be set up before this function is called. */
4520    if( SCIPbendersSubproblem(benders, probnumber) != NULL && !SCIPbendersSubproblemIsSetup(benders, probnumber)
4521       && !SCIPbendersSubproblemIsIndependent(benders, probnumber) )
4522    {
4523       SCIPerrorMessage("Benders' decomposition subproblem %d must be set up before calling SCIPbendersSolveSubproblem(). Call SCIPsetupSubproblem() first.\n", probnumber);
4524       return SCIP_ERROR;
4525    }
4526 
4527    /* if the subproblem solve callback is implemented, then that is used instead of the default setup */
4528    if( benders->benderssolvesubconvex != NULL ||  benders->benderssolvesub != NULL)
4529    {
4530       SCIP_BENDERSSOLVELOOP solveloop;
4531       SCIP_RESULT result;
4532       SCIP_Real subobj;
4533 
4534       if( solvecip )
4535          solveloop = SCIP_BENDERSSOLVELOOP_USERCIP;
4536       else
4537          solveloop = SCIP_BENDERSSOLVELOOP_USERCONVEX;
4538 
4539       SCIP_CALL( executeUserDefinedSolvesub(benders, set, sol, probnumber, solveloop, infeasible, &subobj, &result) );
4540 
4541       if( objective != NULL )
4542          (*objective) = subobj;
4543    }
4544    else
4545    {
4546       SCIP* subproblem;
4547 
4548       subproblem = SCIPbendersSubproblem(benders, probnumber);
4549       assert(subproblem != NULL);
4550 
4551       /* solving the subproblem */
4552       if( solvecip && SCIPbendersGetSubproblemType(benders, probnumber) != SCIP_BENDERSSUBTYPE_CONVEXCONT )
4553       {
4554          SCIP_STATUS solvestatus;
4555 
4556          SCIP_CALL( SCIPbendersSolveSubproblemCIP(set->scip, benders, probnumber, &solvestatus, solvecip) );
4557 
4558          if( solvestatus == SCIP_STATUS_INFEASIBLE )
4559             (*infeasible) = TRUE;
4560          if( objective != NULL )
4561             (*objective) = SCIPgetSolOrigObj(subproblem, SCIPgetBestSol(subproblem))*(int)SCIPgetObjsense(subproblem);
4562       }
4563       else
4564       {
4565          SCIP_Bool success;
4566 
4567          /* if the subproblem has convex constraints and continuous variables, then it should have been initialised and
4568           * in SCIP_STAGE_SOLVING. In this case, the subproblem only needs to be put into probing mode.
4569           */
4570          if( SCIPbendersGetSubproblemType(benders, probnumber) == SCIP_BENDERSSUBTYPE_CONVEXCONT )
4571          {
4572             /* if the subproblem is not in probing mode, then it must be put into that mode for the LP solve. */
4573             if( !SCIPinProbing(subproblem) )
4574             {
4575                SCIP_CALL( SCIPstartProbing(subproblem) );
4576             }
4577 
4578             success = TRUE;
4579          }
4580          else
4581          {
4582             SCIP_CALL( initialiseSubproblem(benders, set, probnumber, &success) );
4583          }
4584 
4585          /* if setting up the subproblem was successful */
4586          if( success )
4587          {
4588             SCIP_STATUS solvestatus;
4589             SCIP_Real lpobjective;
4590 
4591             SCIP_CALL( SCIPbendersSolveSubproblemLP(set->scip, benders, probnumber, &solvestatus, &lpobjective) );
4592 
4593             if( solvestatus == SCIP_STATUS_INFEASIBLE )
4594                (*infeasible) = TRUE;
4595             else if( objective != NULL )
4596                (*objective) = lpobjective;
4597          }
4598          else
4599          {
4600             if( objective != NULL )
4601                (*objective) = SCIPinfinity(subproblem);
4602          }
4603       }
4604    }
4605 
4606    return SCIP_OKAY;
4607 }
4608 
4609 /** copies the time and memory limit from the master problem to the subproblem */
4610 static
copyMemoryAndTimeLimits(SCIP * scip,SCIP * subproblem)4611 SCIP_RETCODE copyMemoryAndTimeLimits(
4612    SCIP*                 scip,               /**< the SCIP data structure */
4613    SCIP*                 subproblem          /**< the Benders' decomposition subproblem */
4614    )
4615 {
4616    SCIP_Real mastertimelimit;
4617    SCIP_Real subtimelimit;
4618    SCIP_Real maxsubtimelimit;
4619    SCIP_Real mastermemorylimit;
4620    SCIP_Real submemorylimit;
4621    SCIP_Real maxsubmemorylimit;
4622 
4623    assert(scip != NULL);
4624 
4625    /* setting the time limit for the Benders' decomposition subproblems. It is set to 102% of the remaining time. */
4626    SCIP_CALL( SCIPgetRealParam(scip, "limits/time", &mastertimelimit) );
4627    maxsubtimelimit = SCIPparamGetRealMax(SCIPgetParam(subproblem, "limits/time"));
4628    subtimelimit = (mastertimelimit - SCIPgetSolvingTime(scip)) * 1.02;
4629    subtimelimit = MIN(subtimelimit, maxsubtimelimit);
4630    SCIP_CALL( SCIPsetRealParam(subproblem, "limits/time", MAX(0.0, subtimelimit)) );
4631 
4632    /* setting the memory limit for the Benders' decomposition subproblems. */
4633    SCIP_CALL( SCIPgetRealParam(scip, "limits/memory", &mastermemorylimit) );
4634    maxsubmemorylimit = SCIPparamGetRealMax(SCIPgetParam(subproblem, "limits/memory"));
4635    submemorylimit = mastermemorylimit - (SCIPgetMemUsed(scip) + SCIPgetMemExternEstim(scip))/1048576.0;
4636    submemorylimit = MIN(submemorylimit, maxsubmemorylimit);
4637    SCIP_CALL( SCIPsetRealParam(subproblem, "limits/memory", MAX(0.0, submemorylimit)) );
4638 
4639    return SCIP_OKAY;
4640 }
4641 
4642 /** stores the original parameters from the subproblem */
4643 static
storeOrigSubproblemParams(SCIP * subproblem,SCIP_SUBPROBPARAMS * origparams)4644 SCIP_RETCODE storeOrigSubproblemParams(
4645    SCIP*                 subproblem,         /**< the SCIP data structure */
4646    SCIP_SUBPROBPARAMS*   origparams          /**< the original subproblem parameters */
4647    )
4648 {
4649    assert(subproblem != NULL);
4650    assert(origparams != NULL);
4651 
4652    SCIP_CALL( SCIPgetRealParam(subproblem, "limits/memory", &origparams->limits_memory) );
4653    SCIP_CALL( SCIPgetRealParam(subproblem, "limits/time", &origparams->limits_time) );
4654    SCIP_CALL( SCIPgetBoolParam(subproblem, "conflict/enable", &origparams->conflict_enable) );
4655    SCIP_CALL( SCIPgetIntParam(subproblem, "lp/disablecutoff", &origparams->lp_disablecutoff) );
4656    SCIP_CALL( SCIPgetIntParam(subproblem, "lp/scaling", &origparams->lp_scaling) );
4657    SCIP_CALL( SCIPgetCharParam(subproblem, "lp/initalgorithm", &origparams->lp_initalg) );
4658    SCIP_CALL( SCIPgetCharParam(subproblem, "lp/resolvealgorithm", &origparams->lp_resolvealg) );
4659    SCIP_CALL( SCIPgetBoolParam(subproblem, "lp/alwaysgetduals", &origparams->lp_alwaysgetduals) );
4660    SCIP_CALL( SCIPgetBoolParam(subproblem, "misc/scaleobj", &origparams->misc_scaleobj) );
4661    SCIP_CALL( SCIPgetBoolParam(subproblem, "misc/catchctrlc", &origparams->misc_catchctrlc) );
4662    SCIP_CALL( SCIPgetIntParam(subproblem, "propagating/maxrounds", &origparams->prop_maxrounds) );
4663    SCIP_CALL( SCIPgetIntParam(subproblem, "propagating/maxroundsroot", &origparams->prop_maxroundsroot) );
4664    SCIP_CALL( SCIPgetIntParam(subproblem, "constraints/linear/propfreq", &origparams->cons_linear_propfreq) );
4665 
4666    return SCIP_OKAY;
4667 }
4668 
4669 /** sets the parameters for the subproblem */
4670 static
setSubproblemParams(SCIP * scip,SCIP * subproblem)4671 SCIP_RETCODE setSubproblemParams(
4672    SCIP*                 scip,               /**< the SCIP data structure */
4673    SCIP*                 subproblem          /**< the subproblem SCIP instance */
4674    )
4675 {
4676    assert(scip != NULL);
4677    assert(subproblem != NULL);
4678 
4679    /* copying memory and time limits */
4680    SCIP_CALL( copyMemoryAndTimeLimits(scip, subproblem) );
4681 
4682    /* Do we have to disable presolving? If yes, we have to store all presolving parameters. */
4683    SCIP_CALL( SCIPsetPresolving(subproblem, SCIP_PARAMSETTING_OFF, TRUE) );
4684 
4685    /* Disabling heuristics so that the problem is not trivially solved */
4686    SCIP_CALL( SCIPsetHeuristics(subproblem, SCIP_PARAMSETTING_OFF, TRUE) );
4687 
4688    /* store parameters that are changed for the generation of the subproblem cuts */
4689    SCIP_CALL( SCIPsetBoolParam(subproblem, "conflict/enable", FALSE) );
4690 
4691    SCIP_CALL( SCIPsetIntParam(subproblem, "lp/disablecutoff", 1) );
4692    SCIP_CALL( SCIPsetIntParam(subproblem, "lp/scaling", 0) );
4693 
4694    SCIP_CALL( SCIPsetCharParam(subproblem, "lp/initalgorithm", 'd') );
4695    SCIP_CALL( SCIPsetCharParam(subproblem, "lp/resolvealgorithm", 'd') );
4696 
4697    SCIP_CALL( SCIPsetBoolParam(subproblem, "lp/alwaysgetduals", TRUE) );
4698    SCIP_CALL( SCIPsetBoolParam(subproblem, "misc/scaleobj", FALSE) );
4699 
4700    /* do not abort subproblem on CTRL-C */
4701    SCIP_CALL( SCIPsetBoolParam(subproblem, "misc/catchctrlc", FALSE) );
4702 
4703    SCIP_CALL( SCIPsetIntParam(subproblem, "display/verblevel", (int)SCIP_VERBLEVEL_NONE) );
4704 
4705    SCIP_CALL( SCIPsetIntParam(subproblem, "propagating/maxrounds", 0) );
4706    SCIP_CALL( SCIPsetIntParam(subproblem, "propagating/maxroundsroot", 0) );
4707 
4708    SCIP_CALL( SCIPsetIntParam(subproblem, "constraints/linear/propfreq", -1) );
4709 
4710    SCIP_CALL( SCIPsetIntParam(subproblem, "heuristics/alns/freq", -1) );
4711 
4712    SCIP_CALL( SCIPsetIntParam(subproblem, "separating/aggregation/freq", -1) );
4713    SCIP_CALL( SCIPsetIntParam(subproblem, "separating/gomory/freq", -1) );
4714 
4715    return SCIP_OKAY;
4716 }
4717 
4718 /** resets the original parameters from the subproblem */
4719 static
resetOrigSubproblemParams(SCIP * subproblem,SCIP_SUBPROBPARAMS * origparams)4720 SCIP_RETCODE resetOrigSubproblemParams(
4721    SCIP*                 subproblem,         /**< the SCIP data structure */
4722    SCIP_SUBPROBPARAMS*   origparams          /**< the original subproblem parameters */
4723    )
4724 {
4725    assert(subproblem != NULL);
4726    assert(origparams != NULL);
4727 
4728    SCIP_CALL( SCIPsetRealParam(subproblem, "limits/memory", origparams->limits_memory) );
4729    SCIP_CALL( SCIPsetRealParam(subproblem, "limits/time", origparams->limits_time) );
4730    SCIP_CALL( SCIPsetBoolParam(subproblem, "conflict/enable", origparams->conflict_enable) );
4731    SCIP_CALL( SCIPsetIntParam(subproblem, "lp/disablecutoff", origparams->lp_disablecutoff) );
4732    SCIP_CALL( SCIPsetIntParam(subproblem, "lp/scaling", origparams->lp_scaling) );
4733    SCIP_CALL( SCIPsetCharParam(subproblem, "lp/initalgorithm", origparams->lp_initalg) );
4734    SCIP_CALL( SCIPsetCharParam(subproblem, "lp/resolvealgorithm", origparams->lp_resolvealg) );
4735    SCIP_CALL( SCIPsetBoolParam(subproblem, "lp/alwaysgetduals", origparams->lp_alwaysgetduals) );
4736    SCIP_CALL( SCIPsetBoolParam(subproblem, "misc/scaleobj", origparams->misc_scaleobj) );
4737    SCIP_CALL( SCIPsetBoolParam(subproblem, "misc/catchctrlc", origparams->misc_catchctrlc) );
4738    SCIP_CALL( SCIPsetIntParam(subproblem, "propagating/maxrounds", origparams->prop_maxrounds) );
4739    SCIP_CALL( SCIPsetIntParam(subproblem, "propagating/maxroundsroot", origparams->prop_maxroundsroot) );
4740    SCIP_CALL( SCIPsetIntParam(subproblem, "constraints/linear/propfreq", origparams->cons_linear_propfreq) );
4741 
4742    return SCIP_OKAY;
4743 }
4744 
4745 /** solves the LP of the Benders' decomposition subproblem
4746  *
4747  *  This requires that the subproblem is in probing mode.
4748  */
SCIPbendersSolveSubproblemLP(SCIP * scip,SCIP_BENDERS * benders,int probnumber,SCIP_STATUS * solvestatus,SCIP_Real * objective)4749 SCIP_RETCODE SCIPbendersSolveSubproblemLP(
4750    SCIP*                 scip,               /**< the SCIP data structure */
4751    SCIP_BENDERS*         benders,            /**< the Benders' decomposition data structure */
4752    int                   probnumber,         /**< the subproblem number */
4753    SCIP_STATUS*          solvestatus,        /**< status of subproblem solve */
4754    SCIP_Real*            objective           /**< optimal value of subproblem, if solved to optimality */
4755    )
4756 {
4757    SCIP* subproblem;
4758    SCIP_SUBPROBPARAMS* origparams;
4759    SCIP_Bool solvenlp;
4760 
4761    assert(benders != NULL);
4762    assert(solvestatus != NULL);
4763    assert(objective != NULL);
4764    assert(SCIPbendersSubproblemIsSetup(benders, probnumber));
4765 
4766    /* TODO: This should be solved just as an LP, so as a MIP. There is too much overhead with the MIP.
4767     * Need to change status check for checking the LP. */
4768    subproblem = SCIPbendersSubproblem(benders, probnumber);
4769    assert(subproblem != NULL);
4770 
4771    /* only solve the NLP relaxation if the NLP has been constructed and there exists an NLPI. If it is not possible to
4772     * solve the NLP relaxation, then the LP relaxation is used to generate Benders' cuts
4773     */
4774    solvenlp = FALSE;
4775    if( SCIPisNLPConstructed(subproblem) && SCIPgetNNlpis(subproblem) > 0
4776       && SCIPbendersGetSubproblemType(benders, probnumber) <= SCIP_BENDERSSUBTYPE_CONVEXDIS )
4777       solvenlp = TRUE;
4778 
4779    *objective = SCIPinfinity(subproblem);
4780 
4781    assert(SCIPisNLPConstructed(subproblem) || SCIPisLPConstructed(subproblem));
4782    assert(SCIPinProbing(subproblem));
4783 
4784    /* allocating memory for the parameter storage */
4785    SCIP_CALL( SCIPallocBlockMemory(subproblem, &origparams) );
4786 
4787    /* store the original parameters of the subproblem */
4788    SCIP_CALL( storeOrigSubproblemParams(subproblem, origparams) );
4789 
4790    /* setting the subproblem parameters */
4791    SCIP_CALL( setSubproblemParams(scip, subproblem) );
4792 
4793    if( solvenlp )
4794    {
4795       SCIP_NLPSOLSTAT nlpsolstat;
4796       SCIP_NLPTERMSTAT nlptermstat;
4797 #ifdef SCIP_MOREDEBUG
4798       SCIP_SOL* nlpsol;
4799 
4800       SCIP_CALL( SCIPsetNLPIntPar(subproblem, SCIP_NLPPAR_VERBLEVEL, 1) );
4801 #endif
4802 
4803       SCIP_CALL( SCIPsetNLPIntPar(subproblem, SCIP_NLPPAR_ITLIM, INT_MAX) );
4804 
4805       SCIP_CALL( SCIPsolveNLP(subproblem) );
4806 
4807       nlpsolstat = SCIPgetNLPSolstat(subproblem);
4808       nlptermstat = SCIPgetNLPTermstat(subproblem);
4809       SCIPdebugMsg(scip, "NLP solstat %d termstat %d\n", nlpsolstat, nlptermstat);
4810 
4811       if( nlptermstat == SCIP_NLPTERMSTAT_OKAY && (nlpsolstat == SCIP_NLPSOLSTAT_LOCINFEASIBLE || nlpsolstat == SCIP_NLPSOLSTAT_GLOBINFEASIBLE) )
4812       {
4813          /* trust infeasible only if terminated "okay" */
4814          (*solvestatus) = SCIP_STATUS_INFEASIBLE;
4815       }
4816       else if( nlpsolstat == SCIP_NLPSOLSTAT_LOCOPT || nlpsolstat == SCIP_NLPSOLSTAT_GLOBOPT
4817          || nlpsolstat == SCIP_NLPSOLSTAT_FEASIBLE )
4818       {
4819 #ifdef SCIP_MOREDEBUG
4820          SCIP_CALL( SCIPcreateNLPSol(subproblem, &nlpsol, NULL) );
4821          SCIP_CALL( SCIPprintSol(subproblem, nlpsol, NULL, FALSE) );
4822          SCIP_CALL( SCIPfreeSol(subproblem, &nlpsol) );
4823 #endif
4824 
4825          (*solvestatus) = SCIP_STATUS_OPTIMAL;
4826          (*objective) = SCIPretransformObj(subproblem, SCIPgetNLPObjval(subproblem));
4827       }
4828       else if( nlpsolstat == SCIP_NLPSOLSTAT_UNBOUNDED )
4829       {
4830          (*solvestatus) = SCIP_STATUS_UNBOUNDED;
4831          SCIPerrorMessage("The NLP of Benders' decomposition subproblem %d is unbounded. This should not happen.\n",
4832             probnumber);
4833          SCIPABORT();
4834       }
4835       else if( nlptermstat == SCIP_NLPTERMSTAT_TILIM )
4836       {
4837          (*solvestatus) = SCIP_STATUS_TIMELIMIT;
4838       }
4839       else
4840       {
4841          SCIPerrorMessage("Invalid solution status: %d. Termination status: %d. Solving the NLP relaxation of Benders' decomposition subproblem %d.\n",
4842             nlpsolstat, nlptermstat, probnumber);
4843          SCIPABORT();
4844       }
4845    }
4846    else
4847    {
4848       SCIP_Bool lperror;
4849       SCIP_Bool cutoff;
4850 
4851       SCIP_CALL( SCIPsolveProbingLP(subproblem, -1, &lperror, &cutoff) );
4852 
4853       switch( SCIPgetLPSolstat(subproblem) )
4854       {
4855          case SCIP_LPSOLSTAT_INFEASIBLE:
4856          {
4857             (*solvestatus) = SCIP_STATUS_INFEASIBLE;
4858             break;
4859          }
4860 
4861          case SCIP_LPSOLSTAT_OPTIMAL :
4862          {
4863             (*solvestatus) = SCIP_STATUS_OPTIMAL;
4864             (*objective) = SCIPgetSolOrigObj(subproblem, NULL)*(int)SCIPgetObjsense(scip);
4865             break;
4866          }
4867 
4868          case SCIP_LPSOLSTAT_UNBOUNDEDRAY :
4869          {
4870             (*solvestatus) = SCIP_STATUS_UNBOUNDED;
4871             SCIPerrorMessage("The LP of Benders' decomposition subproblem %d is unbounded. This should not happen.\n",
4872                probnumber);
4873             SCIPABORT();
4874             break;
4875          }
4876 
4877          case SCIP_LPSOLSTAT_ERROR :
4878          case SCIP_LPSOLSTAT_NOTSOLVED :
4879          case SCIP_LPSOLSTAT_TIMELIMIT :
4880          {
4881             if( SCIPgetLPSolstat(subproblem) == SCIP_LPSOLSTAT_TIMELIMIT )
4882                (*solvestatus) = SCIP_STATUS_TIMELIMIT;
4883             else
4884                (*solvestatus) = SCIP_STATUS_UNKNOWN;
4885 
4886             SCIPverbMessage(scip, SCIP_VERBLEVEL_FULL, NULL, "   Benders' decomposition: Error solving LP "
4887                "relaxation of subproblem %d. No cut will be generated for this subproblem.\n", probnumber);
4888             break;
4889          }
4890 
4891          case SCIP_LPSOLSTAT_OBJLIMIT:
4892          case SCIP_LPSOLSTAT_ITERLIMIT:
4893          default:
4894          {
4895             SCIPerrorMessage("Invalid status: %d. Solving the LP relaxation of Benders' decomposition subproblem %d.\n",
4896                SCIPgetLPSolstat(subproblem), probnumber);
4897             SCIPABORT();
4898             break;
4899          }
4900       }
4901    }
4902 
4903    /* resetting the subproblem parameters */
4904    SCIP_CALL( resetOrigSubproblemParams(subproblem, origparams) );
4905 
4906    /* freeing the parameter storage */
4907    SCIPfreeBlockMemory(subproblem, &origparams);
4908 
4909    return SCIP_OKAY;
4910 }
4911 
4912 /** solves the Benders' decomposition subproblem */
SCIPbendersSolveSubproblemCIP(SCIP * scip,SCIP_BENDERS * benders,int probnumber,SCIP_STATUS * solvestatus,SCIP_Bool solvecip)4913 SCIP_RETCODE SCIPbendersSolveSubproblemCIP(
4914    SCIP*                 scip,               /**< the SCIP data structure */
4915    SCIP_BENDERS*         benders,            /**< the Benders' decomposition data structure */
4916    int                   probnumber,         /**< the subproblem number */
4917    SCIP_STATUS*          solvestatus,        /**< status of subproblem solve */
4918    SCIP_Bool             solvecip            /**< directly solve the CIP subproblem */
4919    )
4920 {
4921    SCIP* subproblem;
4922    SCIP_SUBPROBPARAMS* origparams;
4923 
4924    assert(benders != NULL);
4925    assert(solvestatus != NULL);
4926 
4927    subproblem = SCIPbendersSubproblem(benders, probnumber);
4928    assert(subproblem != NULL);
4929 
4930    /* allocating memory for the parameter storage */
4931    SCIP_CALL( SCIPallocBlockMemory(subproblem, &origparams) );
4932 
4933    /* store the original parameters of the subproblem */
4934    SCIP_CALL( storeOrigSubproblemParams(subproblem, origparams) );
4935 
4936    /* If the solve has been stopped for the subproblem, then we need to restart it to complete the solve. The subproblem
4937     * is stopped when it is a MIP so that LP cuts and IP cuts can be generated. */
4938    if( SCIPgetStage(subproblem) == SCIP_STAGE_SOLVING )
4939    {
4940       /* the subproblem should be in probing mode. Otherwise, the event handler did not work correctly */
4941       assert( SCIPinProbing(subproblem) );
4942 
4943       /* the probing mode needs to be stopped so that the MIP can be solved */
4944       SCIP_CALL( SCIPendProbing(subproblem) );
4945 
4946       /* the problem was interrupted in the event handler, so SCIP needs to be informed that the problem is to be restarted */
4947       SCIP_CALL( SCIPrestartSolve(subproblem) );
4948    }
4949    else if( solvecip )
4950    {
4951       /* if the MIP will be solved directly, then the probing mode needs to be skipped.
4952        * This is achieved by setting the solvecip flag in the event handler data to TRUE
4953        */
4954       SCIP_EVENTHDLR* eventhdlr;
4955       SCIP_EVENTHDLRDATA* eventhdlrdata;
4956 
4957       eventhdlr = SCIPfindEventhdlr(subproblem, MIPNODEFOCUS_EVENTHDLR_NAME);
4958       eventhdlrdata = SCIPeventhdlrGetData(eventhdlr);
4959 
4960       eventhdlrdata->solvecip = TRUE;
4961    }
4962    else
4963    {
4964       /* if the problem is not in probing mode, then we need to solve the LP. That requires all methods that will
4965        * modify the structure of the problem need to be deactivated */
4966 
4967       /* setting the subproblem parameters */
4968       SCIP_CALL( setSubproblemParams(scip, subproblem) );
4969 
4970 #ifdef SCIP_EVENMOREDEBUG
4971       SCIP_CALL( SCIPsetBoolParam(subproblem, "display/lpinfo", TRUE) );
4972 #endif
4973    }
4974 
4975 #ifdef SCIP_MOREDEBUG
4976       SCIP_CALL( SCIPsetIntParam(subproblem, "display/verblevel", (int)SCIP_VERBLEVEL_FULL) );
4977       SCIP_CALL( SCIPsetIntParam(subproblem, "display/freq", 1) );
4978 #endif
4979 
4980    SCIP_CALL( SCIPsolve(subproblem) );
4981 
4982    *solvestatus = SCIPgetStatus(subproblem);
4983 
4984    if( *solvestatus != SCIP_STATUS_OPTIMAL && *solvestatus != SCIP_STATUS_UNBOUNDED
4985       && *solvestatus != SCIP_STATUS_INFEASIBLE && *solvestatus != SCIP_STATUS_USERINTERRUPT
4986       && *solvestatus != SCIP_STATUS_BESTSOLLIMIT && *solvestatus != SCIP_STATUS_TIMELIMIT
4987       && *solvestatus != SCIP_STATUS_MEMLIMIT )
4988    {
4989       SCIPerrorMessage("Invalid status: %d. Solving the CIP of Benders' decomposition subproblem %d.\n",
4990          *solvestatus, probnumber);
4991       SCIPABORT();
4992    }
4993 
4994    /* resetting the subproblem parameters */
4995    SCIP_CALL( resetOrigSubproblemParams(subproblem, origparams) );
4996 
4997    /* freeing the parameter storage */
4998    SCIPfreeBlockMemory(subproblem, &origparams);
4999 
5000    return SCIP_OKAY;
5001 }
5002 
5003 /** frees the subproblems */
SCIPbendersFreeSubproblem(SCIP_BENDERS * benders,SCIP_SET * set,int probnumber)5004 SCIP_RETCODE SCIPbendersFreeSubproblem(
5005    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
5006    SCIP_SET*             set,                /**< global SCIP settings */
5007    int                   probnumber          /**< the subproblem number */
5008    )
5009 {
5010    assert(benders != NULL);
5011    assert(benders->bendersfreesub != NULL
5012       || (benders->bendersfreesub == NULL && benders->benderssolvesubconvex == NULL && benders->benderssolvesub == NULL));
5013    assert(probnumber >= 0 && probnumber < benders->nsubproblems);
5014 
5015    if( benders->bendersfreesub != NULL )
5016    {
5017       SCIP_CALL( benders->bendersfreesub(set->scip, benders, probnumber) );
5018    }
5019    else
5020    {
5021       /* the subproblem is only freed if it is not independent */
5022       if( subproblemIsActive(benders, probnumber) )
5023       {
5024          SCIP* subproblem = SCIPbendersSubproblem(benders, probnumber);
5025 
5026          if( SCIPbendersGetSubproblemType(benders, probnumber) == SCIP_BENDERSSUBTYPE_CONVEXCONT )
5027          {
5028             /* ending probing mode to reset the current node. The probing mode will be restarted at the next solve */
5029             if( SCIPinProbing(subproblem) )
5030             {
5031                SCIP_CALL( SCIPendProbing(subproblem) );
5032             }
5033          }
5034          else
5035          {
5036             /* if the subproblems were solved as part of an enforcement stage, then they will still be in probing mode. The
5037              * probing mode must first be finished and then the problem can be freed */
5038             if( SCIPgetStage(subproblem) >= SCIP_STAGE_TRANSFORMED && SCIPinProbing(subproblem) )
5039             {
5040                SCIP_CALL( SCIPendProbing(subproblem) );
5041             }
5042 
5043             SCIP_CALL( SCIPfreeTransform(subproblem) );
5044          }
5045       }
5046    }
5047 
5048    /* setting the setup flag for the subproblem to FALSE */
5049    SCIPbendersSetSubproblemIsSetup(benders, probnumber, FALSE);
5050    return SCIP_OKAY;
5051 }
5052 
5053 /** compares the subproblem objective value with the auxiliary variable value for optimality */
SCIPbendersSubproblemIsOptimal(SCIP_BENDERS * benders,SCIP_SET * set,SCIP_SOL * sol,int probnumber)5054 SCIP_Bool SCIPbendersSubproblemIsOptimal(
5055    SCIP_BENDERS*         benders,            /**< the benders' decomposition structure */
5056    SCIP_SET*             set,                /**< global SCIP settings */
5057    SCIP_SOL*             sol,                /**< primal CIP solution */
5058    int                   probnumber          /**< the subproblem number */
5059    )
5060 {
5061    SCIP_Real auxiliaryvarval;
5062    SCIP_Bool optimal;
5063 
5064    assert(benders != NULL);
5065    assert(set != NULL);
5066    assert(probnumber >= 0 && probnumber < benders->nsubproblems);
5067 
5068    optimal = FALSE;
5069 
5070    auxiliaryvarval = SCIPbendersGetAuxiliaryVarVal(benders, set, sol, probnumber);
5071 
5072    SCIPsetDebugMsg(set, "Subproblem %d - Auxiliary Variable: %g Subproblem Objective: %g Reldiff: %g Soltol: %g\n",
5073       probnumber, auxiliaryvarval, SCIPbendersGetSubproblemObjval(benders, probnumber),
5074       SCIPrelDiff(SCIPbendersGetSubproblemObjval(benders, probnumber), auxiliaryvarval), benders->solutiontol);
5075 
5076    if( SCIPrelDiff(SCIPbendersGetSubproblemObjval(benders, probnumber), auxiliaryvarval) < benders->solutiontol )
5077       optimal = TRUE;
5078 
5079    return optimal;
5080 }
5081 
5082 /** returns the value of the auxiliary variable value in a master problem solution */
SCIPbendersGetAuxiliaryVarVal(SCIP_BENDERS * benders,SCIP_SET * set,SCIP_SOL * sol,int probnumber)5083 SCIP_Real SCIPbendersGetAuxiliaryVarVal(
5084    SCIP_BENDERS*         benders,            /**< the benders' decomposition structure */
5085    SCIP_SET*             set,                /**< global SCIP settings */
5086    SCIP_SOL*             sol,                /**< primal CIP solution */
5087    int                   probnumber          /**< the subproblem number */
5088    )
5089 {
5090    SCIP_VAR* auxiliaryvar;
5091 
5092    assert(benders != NULL);
5093    assert(set != NULL);
5094 
5095    auxiliaryvar = SCIPbendersGetAuxiliaryVar(benders, probnumber);
5096    assert(auxiliaryvar != NULL);
5097 
5098    return SCIPgetSolVal(set->scip, sol, auxiliaryvar);
5099 }
5100 
5101 /** Solves an independent subproblem to identify its lower bound. The lower bound is then used to update the bound on
5102  *  the auxiliary variable.
5103  */
SCIPbendersComputeSubproblemLowerbound(SCIP_BENDERS * benders,SCIP_SET * set,int probnumber,SCIP_Real * lowerbound,SCIP_Bool * infeasible)5104 SCIP_RETCODE SCIPbendersComputeSubproblemLowerbound(
5105    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
5106    SCIP_SET*             set,                /**< global SCIP settings */
5107    int                   probnumber,         /**< the subproblem to be evaluated */
5108    SCIP_Real*            lowerbound,         /**< the lowerbound for the subproblem */
5109    SCIP_Bool*            infeasible          /**< was the subproblem found to be infeasible? */
5110    )
5111 {
5112    SCIP* subproblem;
5113    SCIP_Real dualbound;
5114    SCIP_Real memorylimit;
5115    SCIP_Real timelimit;
5116    SCIP_Longint totalnodes;
5117    int disablecutoff;
5118    int verblevel;
5119    SCIP_Bool lperror;
5120    SCIP_Bool cutoff;
5121 
5122    assert(benders != NULL);
5123    assert(set != NULL);
5124 
5125    if( benders->benderssolvesub != NULL || benders->benderssolvesubconvex != NULL )
5126    {
5127       (*lowerbound) = SCIPvarGetLbGlobal(SCIPbendersGetAuxiliaryVar(benders, probnumber));
5128       (*infeasible) = FALSE;
5129 
5130       SCIPinfoMessage(set->scip, NULL, "Benders' decomposition: a bendersSolvesub or bendersSolvesubconvex has been "
5131          "implemented. SCIPbendersComputeSubproblemLowerbound can not be executed.\n");
5132       SCIPinfoMessage(set->scip, NULL, "Set the auxiliary variable lower bound by calling "
5133          "SCIPbendersUpdateSubproblemLowerbound in bendersCreatesub. The auxiliary variable %d will remain as %g\n",
5134          probnumber, (*lowerbound));
5135 
5136       return SCIP_OKAY;
5137    }
5138    else
5139    {
5140       SCIPverbMessage(set->scip, SCIP_VERBLEVEL_FULL, NULL, "Benders' decomposition: Computing a lower bound for"
5141          " subproblem %d\n", probnumber);
5142    }
5143 
5144    /* getting the subproblem to evaluate */
5145    subproblem = SCIPbendersSubproblem(benders, probnumber);
5146 
5147    (*lowerbound) = -SCIPinfinity(subproblem);
5148    (*infeasible) = FALSE;
5149 
5150    SCIP_CALL( SCIPgetIntParam(subproblem, "display/verblevel", &verblevel) );
5151    SCIP_CALL( SCIPsetIntParam(subproblem, "display/verblevel", (int)SCIP_VERBLEVEL_NONE) );
5152 #ifdef SCIP_MOREDEBUG
5153    SCIP_CALL( SCIPsetIntParam(subproblem, "display/verblevel", (int)SCIP_VERBLEVEL_HIGH) );
5154 #endif
5155 
5156    /* copying memory and time limits */
5157    SCIP_CALL( SCIPgetRealParam(subproblem, "limits/time", &timelimit) );
5158    SCIP_CALL( SCIPgetRealParam(subproblem, "limits/memory", &memorylimit) );
5159    SCIP_CALL( copyMemoryAndTimeLimits(set->scip, subproblem) );
5160 
5161    /* if the subproblem is independent, then the default SCIP settings are used. Otherwise, only the root node is solved
5162     * to compute a lower bound on the subproblem
5163     */
5164    SCIP_CALL( SCIPgetLongintParam(subproblem, "limits/totalnodes", &totalnodes) );
5165    SCIP_CALL( SCIPgetIntParam(subproblem, "lp/disablecutoff", &disablecutoff) );
5166    if( !SCIPbendersSubproblemIsIndependent(benders, probnumber) )
5167    {
5168       SCIP_CALL( SCIPsetLongintParam(subproblem, "limits/totalnodes", 1LL) );
5169       SCIP_CALL( SCIPsetIntParam(subproblem, "lp/disablecutoff", 1) );
5170    }
5171 
5172    /* if the subproblem not independent and is convex, then the probing LP is solved. Otherwise, the MIP is solved */
5173    dualbound = -SCIPinfinity(subproblem);
5174    if( SCIPbendersGetSubproblemType(benders, probnumber) == SCIP_BENDERSSUBTYPE_CONVEXCONT )
5175    {
5176       SCIP_Bool solvenlp = FALSE;
5177 
5178       assert(SCIPisLPConstructed(subproblem) || SCIPisNLPConstructed(subproblem));
5179 
5180       if( SCIPisNLPConstructed(subproblem) && SCIPgetNNlpis(subproblem) > 0
5181          && SCIPbendersGetSubproblemType(benders, probnumber) <= SCIP_BENDERSSUBTYPE_CONVEXDIS )
5182          solvenlp = TRUE;
5183 
5184       SCIP_CALL( SCIPstartProbing(subproblem) );
5185       if( solvenlp )
5186       {
5187          SCIP_NLPSOLSTAT nlpsolstat;
5188          SCIP_NLPTERMSTAT nlptermstat;
5189 #ifdef SCIP_MOREDEBUG
5190          SCIP_CALL( SCIPsetNLPIntPar(subproblem, SCIP_NLPPAR_VERBLEVEL, 1) );
5191 #endif
5192 
5193          SCIP_CALL( SCIPsetNLPIntPar(subproblem, SCIP_NLPPAR_ITLIM, INT_MAX) );
5194 
5195          SCIP_CALL( SCIPsolveNLP(subproblem) );
5196 
5197          nlpsolstat = SCIPgetNLPSolstat(subproblem);
5198          nlptermstat = SCIPgetNLPTermstat(subproblem);
5199          SCIPdebugMsg(set->scip, "NLP solstat %d termstat %d\n", nlpsolstat, nlptermstat);
5200 
5201          if( nlptermstat == SCIP_NLPTERMSTAT_OKAY && (nlpsolstat == SCIP_NLPSOLSTAT_LOCINFEASIBLE || nlpsolstat == SCIP_NLPSOLSTAT_GLOBINFEASIBLE) )
5202          {
5203             /* trust infeasible only if terminated "okay" */
5204             (*infeasible) = TRUE;
5205          }
5206          else if( nlpsolstat == SCIP_NLPSOLSTAT_LOCOPT || nlpsolstat == SCIP_NLPSOLSTAT_GLOBOPT
5207             || nlpsolstat == SCIP_NLPSOLSTAT_FEASIBLE )
5208          {
5209             dualbound = SCIPretransformObj(subproblem, SCIPgetNLPObjval(subproblem));
5210          }
5211       }
5212       else
5213       {
5214          SCIP_CALL( SCIPsolveProbingLP(subproblem, -1, &lperror, &cutoff) );
5215 
5216          if( SCIPgetLPSolstat(subproblem) == SCIP_LPSOLSTAT_INFEASIBLE )
5217             (*infeasible) = TRUE;
5218          else if( SCIPgetLPSolstat(subproblem) == SCIP_LPSOLSTAT_OPTIMAL )
5219             dualbound = SCIPgetSolOrigObj(subproblem, NULL)*(int)SCIPgetObjsense(set->scip);
5220       }
5221    }
5222    else
5223    {
5224       SCIP_EVENTHDLRDATA* eventhdlrdata;
5225 
5226       /* if the subproblem is not convex, then event handlers have been added to interrupt the solve. These must be
5227        * disabled
5228        */
5229       eventhdlrdata = SCIPeventhdlrGetData(SCIPfindEventhdlr(subproblem, MIPNODEFOCUS_EVENTHDLR_NAME));
5230       eventhdlrdata->solvecip = TRUE;
5231 
5232       SCIP_CALL( SCIPsolve(subproblem) );
5233 
5234       if( SCIPgetStatus(subproblem) == SCIP_STATUS_INFEASIBLE )
5235          (*infeasible) = TRUE;
5236       else
5237          dualbound = SCIPgetDualbound(subproblem);
5238    }
5239 
5240    /* getting the lower bound value */
5241    (*lowerbound) = dualbound;
5242 
5243    if( !SCIPbendersSubproblemIsIndependent(benders, probnumber) )
5244    {
5245       SCIP_CALL( SCIPsetLongintParam(subproblem, "limits/totalnodes", totalnodes) );
5246       SCIP_CALL( SCIPsetIntParam(subproblem, "lp/disablecutoff", disablecutoff) );
5247    }
5248    SCIP_CALL( SCIPsetIntParam(subproblem, "display/verblevel", verblevel) );
5249    SCIP_CALL( SCIPsetRealParam(subproblem, "limits/memory", memorylimit) );
5250    SCIP_CALL( SCIPsetRealParam(subproblem, "limits/time", timelimit) );
5251 
5252    /* the subproblem must be freed so that it is reset for the subsequent Benders' decomposition solves. If the
5253     * subproblems are independent, they are not freed. SCIPfreeBendersSubproblem must still be called, but in this
5254     * function the independent subproblems are not freed. However, they will still be freed at the end of the
5255     * solving process for the master problem.
5256     */
5257    SCIP_CALL( SCIPbendersFreeSubproblem(benders, set, probnumber) );
5258 
5259    return SCIP_OKAY;
5260 }
5261 
5262 /** Merges a subproblem into the master problem. This process just adds a copy of the subproblem variables and
5263  *  constraints to the master problem, but keeps the subproblem stored in the Benders' decomposition data structure. The reason for
5264  *  keeping the subproblem available is for when it is queried for solutions after the problem is solved.
5265  *
5266  *  Once the subproblem is merged into the master problem, then the subproblem is flagged as disabled. This means that
5267  *  it will not be solved in the subsequent subproblem solving loops.
5268  *
5269  *  The associated auxiliary variables are kept in the master problem. The objective function of the merged subproblem
5270  *  is added as an underestimator constraint.
5271  */
SCIPbendersMergeSubproblemIntoMaster(SCIP_BENDERS * benders,SCIP_SET * set,SCIP_HASHMAP * varmap,SCIP_HASHMAP * consmap,int probnumber)5272 SCIP_RETCODE SCIPbendersMergeSubproblemIntoMaster(
5273    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
5274    SCIP_SET*             set,                /**< global SCIP settings */
5275    SCIP_HASHMAP*         varmap,             /**< a hashmap to store the mapping of subproblem variables corresponding
5276                                               *   to the newly created master variables, or NULL */
5277    SCIP_HASHMAP*         consmap,            /**< a hashmap to store the mapping of subproblem constraints to the
5278                                               *   corresponding newly created constraints, or NULL */
5279    int                   probnumber          /**< the number of the subproblem that will be merged into the master problem*/
5280    )
5281 {
5282    SCIP* subproblem;
5283    SCIP_HASHMAP* localvarmap;
5284    SCIP_HASHMAP* localconsmap;
5285    SCIP_VAR** vars;
5286    SCIP_VAR* auxiliaryvar;
5287    SCIP_CONS** conss;
5288    SCIP_CONS* objcons;
5289    int nvars;
5290    int nconss;
5291    int i;
5292    SCIP_Bool uselocalvarmap;
5293    SCIP_Bool uselocalconsmap;
5294    char varname[SCIP_MAXSTRLEN];
5295    char consname[SCIP_MAXSTRLEN];
5296    const char* origvarname;
5297 
5298    assert(benders != NULL);
5299    assert(set != NULL);
5300    assert(probnumber >= 0 && probnumber < benders->nsubproblems);
5301 
5302    SCIPverbMessage(set->scip, SCIP_VERBLEVEL_HIGH, NULL, "   Benders' decomposition: Infeasibility of subproblem %d can't "
5303       "be resolved. Subproblem %d is being merged into the master problem.\n", probnumber, probnumber);
5304 
5305    /* freeing the subproblem because it will be flagged as independent. Since the subproblem is flagged as independent,
5306     * it will no longer be solved or freed within the solving loop.
5307     */
5308    SCIP_CALL( SCIPbendersFreeSubproblem(benders, set, probnumber) );
5309 
5310    subproblem = SCIPbendersSubproblem(benders, probnumber);
5311 
5312    uselocalvarmap = (varmap == NULL);
5313    uselocalconsmap = (consmap == NULL);
5314 
5315    if( uselocalvarmap )
5316    {
5317       /* create the variable mapping hash map */
5318       SCIP_CALL( SCIPhashmapCreate(&localvarmap, SCIPblkmem(set->scip), SCIPgetNVars(subproblem)) );
5319    }
5320    else
5321       localvarmap = varmap;
5322 
5323    if( uselocalconsmap )
5324    {
5325       /* create the constraint mapping hash map */
5326       SCIP_CALL( SCIPhashmapCreate(&localconsmap, SCIPblkmem(set->scip), SCIPgetNConss(subproblem)) );
5327    }
5328    else
5329       localconsmap = consmap;
5330 
5331    /* retrieving the subproblem variable to build a subproblem mapping */
5332    vars = SCIPgetVars(subproblem);
5333    nvars = SCIPgetNVars(subproblem);
5334 
5335    /* creating the objective function constraint that will be added to the master problem */
5336    /* setting the name of the transferred cut */
5337    (void) SCIPsnprintf(consname, SCIP_MAXSTRLEN, "objectivecons_%d", probnumber );
5338    SCIP_CALL( SCIPcreateConsBasicLinear(set->scip, &objcons, consname, 0, NULL, NULL, -SCIPsetInfinity(set), 0.0) );
5339    SCIP_CALL( SCIPsetConsRemovable(set->scip, objcons, TRUE) );
5340 
5341    for( i = 0; i < nvars; i++ )
5342    {
5343       SCIP_VAR* mastervar = NULL;
5344       SCIP_Bool releasevar = FALSE;
5345 
5346       SCIP_CALL( SCIPgetBendersMasterVar(set->scip, benders, vars[i], &mastervar) );
5347 
5348       /* if the master problem variable is not NULL, then there is a corresponding variable in the master problem for
5349        * the given subproblem variable. In this case, the variable is added to the hashmap.
5350        */
5351       if( mastervar == NULL )
5352       {
5353          SCIP_VAR* origvar;
5354          SCIP_Real scalar;
5355          SCIP_Real constant;
5356 
5357          /* This is following the same process as in createVariableMappings. The original variable is used to map
5358           * between the subproblem and the master problem
5359           */
5360          origvar = vars[i];
5361          scalar = 1.0;
5362          constant = 0.0;
5363          SCIP_CALL( SCIPvarGetOrigvarSum(&origvar, &scalar, &constant) );
5364 
5365          /* retrieving the var name */
5366          origvarname = SCIPvarGetName(origvar);
5367          (void) SCIPsnprintf(varname, SCIP_MAXSTRLEN, "%s", origvarname);
5368 
5369          /* creating and adding the variable to the Benders' decomposition master problem */
5370          SCIP_CALL( SCIPcreateVarBasic(set->scip, &mastervar, varname, SCIPvarGetLbOriginal(origvar),
5371             SCIPvarGetUbOriginal(origvar), 0.0, SCIPvarGetType(origvar)) );
5372 
5373          /* adding the variable to the master problem */
5374          SCIP_CALL( SCIPaddVar(set->scip, mastervar) );
5375 
5376          /* adds the variable to the objective function constraint */
5377          SCIP_CALL( SCIPaddCoefLinear(set->scip, objcons, mastervar, SCIPvarGetObj(origvar)) );
5378 
5379          /* the variable must be released */
5380          releasevar = TRUE;
5381       }
5382 
5383       /* creating the mapping betwen the subproblem var and the master var for the constraint copying */
5384       SCIP_CALL( SCIPhashmapInsert(localvarmap, vars[i], mastervar) );
5385 
5386       /* releasing the variable */
5387       if( releasevar )
5388       {
5389          SCIP_CALL( SCIPreleaseVar(set->scip, &mastervar) );
5390       }
5391    }
5392 
5393    /* getting the constraints from the subproblem that will be added to the master problem */
5394    conss = SCIPgetConss(subproblem);
5395    nconss = SCIPgetNConss(subproblem);
5396 
5397    /* getting a copy of all constraints and adding it to the master problem */
5398    for( i = 0; i < nconss; i++ )
5399    {
5400       SCIP_CONS* targetcons;
5401       SCIP_Bool initial;
5402       SCIP_Bool valid;
5403 
5404       /* NOTE: adding all subproblem constraints appears to cause an error when resolving the LP, which results in the
5405        * current incumbent being reported as optimal. To avoid this, only half of the subproblem constraints are added
5406        * the master problem. The remaining half are marked as lazy and are separated as required.
5407        */
5408       initial = (i < nconss/2);
5409 
5410       SCIP_CALL( SCIPgetConsCopy(subproblem, set->scip, conss[i], &targetcons, SCIPconsGetHdlr(conss[i]),
5411          localvarmap, localconsmap, NULL, initial, SCIPconsIsSeparated(conss[i]),
5412          SCIPconsIsEnforced(conss[i]), SCIPconsIsChecked(conss[i]), SCIPconsIsPropagated(conss[i]), FALSE,
5413          SCIPconsIsModifiable(conss[i]), SCIPconsIsDynamic(conss[i]), SCIPconsIsRemovable(conss[i]),
5414          FALSE, TRUE, &valid) );
5415       assert(SCIPconsIsInitial(conss[i]));
5416       assert(valid);
5417 
5418       SCIP_CALL( SCIPaddCons(set->scip, targetcons) );
5419 
5420       SCIP_CALL( SCIPreleaseCons(set->scip, &targetcons) );
5421    }
5422 
5423    /* freeing the hashmaps */
5424    if( uselocalvarmap )
5425    {
5426       /* free hash map */
5427       SCIPhashmapFree(&localvarmap);
5428    }
5429 
5430    if( uselocalconsmap )
5431    {
5432       /* free hash map */
5433       SCIPhashmapFree(&localconsmap);
5434    }
5435 
5436    /* adding the auxiliary variable to the objective constraint */
5437    auxiliaryvar = SCIPbendersGetAuxiliaryVar(benders, probnumber);
5438    SCIP_CALL( SCIPaddCoefLinear(set->scip, objcons, auxiliaryvar, -1.0) );
5439 
5440    /* adding the objective function constraint to the master problem */
5441    SCIP_CALL( SCIPaddCons(set->scip, objcons) );
5442 
5443    SCIP_CALL( SCIPreleaseCons(set->scip, &objcons) );
5444 
5445    /* the merged subproblem is no longer solved. This is indicated by setting the subproblem as disabled. The
5446     * subproblem still exists, but it is not solved in the solving loop.
5447     */
5448    SCIPbendersSetSubproblemEnabled(benders, probnumber, FALSE);
5449 
5450    return SCIP_OKAY;
5451 }
5452 
5453 /** when applying a decomposition from a supplied format, constraints must be transferred from the master problem to the
5454  *  subproblem. This is achieved by adding new constraints to the subproblem
5455  */
5456 static
addConstraintToBendersSubproblem(SCIP_SET * set,SCIP * subproblem,SCIP_HASHMAP * varmap,SCIP_CONS * sourcecons)5457 SCIP_RETCODE addConstraintToBendersSubproblem(
5458    SCIP_SET*             set,                /**< global SCIP settings */
5459    SCIP*                 subproblem,         /**< the SCIP instance for the subproblem */
5460    SCIP_HASHMAP*         varmap,             /**< the variable hash map mapping the source variables to the target variables */
5461    SCIP_CONS*            sourcecons          /**< the constraint that being added to the subproblem */
5462    )
5463 {
5464    SCIP* scip;
5465    SCIP_CONS* cons;
5466    SCIP_VAR** consvars;
5467    int nconsvars;
5468    int i;
5469    SCIP_Bool success;
5470 
5471    assert(set != NULL);
5472    assert(subproblem != NULL);
5473    assert(varmap != NULL);
5474    assert(sourcecons != NULL);
5475 
5476    SCIPdebugMessage("Adding constraint <%s> to Benders' decomposition subproblem\n", SCIPconsGetName(sourcecons));
5477 
5478    scip = set->scip;
5479 
5480    /* getting the variables that are in the constraint */
5481    SCIP_CALL( SCIPgetConsNVars(scip, sourcecons, &nconsvars, &success) );
5482    SCIP_CALL( SCIPallocBufferArray(scip, &consvars, nconsvars) );
5483 
5484    SCIP_CALL( SCIPgetConsVars(scip, sourcecons, consvars, nconsvars, &success) );
5485    assert(success);
5486 
5487    /* checking all variables to see whether they already exist in the subproblem. If they don't exist, then the variable
5488     * is created
5489     */
5490    for( i = 0; i < nconsvars; i++ )
5491    {
5492       /* if the variable is not in the hashmap, then it doesn't exist in the subproblem */
5493       if( !SCIPhashmapExists(varmap, consvars[i]) )
5494       {
5495          SCIP_VAR* var;
5496 
5497          /* creating a variable as a copy of the original variable. */
5498          SCIP_CALL( SCIPcreateVar(subproblem, &var, SCIPvarGetName(consvars[i]), SCIPvarGetLbGlobal(consvars[i]),
5499                SCIPvarGetUbGlobal(consvars[i]), SCIPvarGetObj(consvars[i]), SCIPvarGetType(consvars[i]),
5500                SCIPvarIsInitial(consvars[i]), SCIPvarIsRemovable(consvars[i]), NULL, NULL, NULL, NULL, NULL) );
5501 
5502          /* adding the variable to the subproblem */
5503          SCIP_CALL( SCIPaddVar(subproblem, var) );
5504 
5505          /* adding the variable to the hash map so that it is copied correctly in the constraint */
5506          SCIP_CALL( SCIPhashmapInsert(varmap, consvars[i], var) );
5507 
5508          /* releasing the variable */
5509          SCIP_CALL( SCIPreleaseVar(subproblem, &var) );
5510       }
5511    }
5512 
5513    /* freeing the buffer memory for the consvars */
5514    SCIPfreeBufferArray(scip, &consvars);
5515 
5516    /* copying the constraint from the master scip to the subproblem */
5517    SCIP_CALL( SCIPgetConsCopy(scip, subproblem, sourcecons, &cons, SCIPconsGetHdlr(sourcecons), varmap, NULL,
5518          SCIPconsGetName(sourcecons), SCIPconsIsInitial(sourcecons), SCIPconsIsSeparated(sourcecons),
5519          SCIPconsIsEnforced(sourcecons), SCIPconsIsChecked(sourcecons), SCIPconsIsMarkedPropagate(sourcecons),
5520          SCIPconsIsLocal(sourcecons), SCIPconsIsModifiable(sourcecons), SCIPconsIsDynamic(sourcecons),
5521          SCIPconsIsRemovable(sourcecons), SCIPconsIsStickingAtNode(sourcecons), TRUE, &success) );
5522 
5523    /* if the copy failed, then the subproblem for the decomposition could not be performed. */
5524    if( !success )
5525    {
5526       SCIPerrorMessage("It is not possible to copy constraint <%s>. Benders' decomposition could not be applied.\n",
5527          SCIPconsGetName(sourcecons));
5528       return SCIP_ERROR;
5529    }
5530 
5531    SCIP_CALL( SCIPaddCons(subproblem, cons) );
5532    SCIP_CALL( SCIPreleaseCons(subproblem, &cons) );
5533 
5534    return SCIP_OKAY;
5535 }
5536 
5537 /** removes the variables and constraints from the master problem that have been transferred to a subproblem when the
5538  *  decomposition was applied.
5539  */
5540 static
removeVariablesAndConstraintsFromMaster(SCIP * scip,SCIP_CONS ** conss,SCIP_VAR ** vars,int * conslabels,int * varslabels,int nconss,int nvars)5541 SCIP_RETCODE removeVariablesAndConstraintsFromMaster(
5542    SCIP*                 scip,               /**< the SCIP data structure */
5543    SCIP_CONS**           conss,              /**< the master problem constraints */
5544    SCIP_VAR**            vars,               /**< the master problem variables, can be NULL */
5545    int*                  conslabels,         /**< the labels indicating the block for each constraint */
5546    int*                  varslabels,         /**< the labels indicating the block for each variable, can be NULL */
5547    int                   nconss,             /**< the number of constraints */
5548    int                   nvars               /**< the number of variables */
5549    )
5550 {
5551    int i;
5552 
5553    assert(scip != NULL);
5554    assert(conss != NULL);
5555    assert(conslabels != NULL);
5556    assert((vars != NULL && varslabels != NULL) || (vars == NULL && varslabels == NULL));
5557 
5558    /* removing constraints */
5559    for( i = nconss - 1; i >= 0; i-- )
5560    {
5561       if( conslabels[i] >= 0 && !SCIPconsIsDeleted(conss[i]) )
5562          SCIP_CALL( SCIPdelCons(scip, conss[i]) );
5563    }
5564 
5565    /* removing variables */
5566    if( SCIPgetStage(scip) == SCIP_STAGE_PROBLEM && vars != NULL && varslabels != NULL )
5567    {
5568       for( i = nvars - 1; i >= 0; i-- )
5569       {
5570          if( varslabels[i] >= 0 && !SCIPvarIsDeleted(vars[i]) )
5571          {
5572             SCIP_Bool deleted;
5573 
5574             SCIP_CALL( SCIPdelVar(scip, vars[i], &deleted) );
5575             assert(deleted);
5576          }
5577       }
5578    }
5579 
5580    return SCIP_OKAY;
5581 }
5582 
5583 /** Applies a Benders' decomposition to the problem based upon the decomposition selected from the storage */
SCIPbendersApplyDecomposition(SCIP_BENDERS * benders,SCIP_SET * set,SCIP_DECOMP * decomp)5584 SCIP_RETCODE SCIPbendersApplyDecomposition(
5585    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
5586    SCIP_SET*             set,                /**< global SCIP settings */
5587    SCIP_DECOMP*          decomp              /**< the decomposition to apply to the problem */
5588    )
5589 {
5590    SCIP** subproblems;
5591    SCIP_VAR** vars;
5592    SCIP_CONS** conss;
5593    SCIP_HASHMAP** varmaps;
5594    int* varslabels;
5595    int* conslabels;
5596    int nvars;
5597    int nconss;
5598    int nblocks;
5599    int i;
5600    char subprobname[SCIP_MAXSTRLEN];
5601 
5602    assert(benders != NULL);
5603    assert(set != NULL);
5604    assert(decomp != NULL);
5605 
5606    SCIPdebugMessage("Applying a Benders' decomposition to <%s>\n", SCIPgetProbName(set->scip));
5607 
5608    /* retrieving the number of blocks for this decomposition */
5609    nblocks = SCIPdecompGetNBlocks(decomp);
5610    assert(nblocks > 0);
5611 
5612    /* initialising the subproblems for the Benders' decomposition */
5613    SCIP_CALL( SCIPallocBufferArray(set->scip, &subproblems, nblocks) );
5614 
5615    /* creating the subproblems before adding the constraints */
5616    for( i = 0; i < nblocks; i++ )
5617    {
5618       SCIP_Bool valid;
5619 
5620       SCIP_CALL( SCIPcreate(&subproblems[i]) );
5621 
5622       /* copying the plugins from the original SCIP instance to the subproblem SCIP */
5623       SCIP_CALL( SCIPcopyPlugins(set->scip, subproblems[i], TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
5624             TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, &valid) );
5625 
5626       (void) SCIPsnprintf(subprobname, SCIP_MAXSTRLEN, "sub_%s_%d", SCIPgetProbName(set->scip), i);
5627       SCIP_CALL( SCIPcreateProbBasic(subproblems[i], subprobname) );
5628    }
5629 
5630    /* TODO: Need to work out whether a check for original and transformed problem is necessary */
5631 
5632    /* getting the variables and constraints from the problem */
5633    SCIP_CALL( SCIPgetVarsData(set->scip, &vars, &nvars, NULL, NULL, NULL, NULL) );
5634    conss = SCIPgetConss(set->scip);
5635    nconss = SCIPgetNConss(set->scip);
5636 
5637    /* allocating buffer memory for the labels arrays */
5638    SCIP_CALL( SCIPallocBufferArray(set->scip, &varslabels, nvars) );
5639    SCIP_CALL( SCIPallocBufferArray(set->scip, &conslabels, nconss) );
5640 
5641    /* getting the labels for the variables and constraints from the decomposition */
5642    SCIPdecompGetVarsLabels(decomp, vars, varslabels, nvars);
5643    SCIPdecompGetConsLabels(decomp, conss, conslabels, nconss);
5644 
5645    /* creating the variable maps for adding the constraints to the subproblems */
5646    SCIP_CALL( SCIPallocBufferArray(set->scip, &varmaps, nblocks) );
5647 
5648    for( i = 0; i < nblocks; i++ )
5649    {
5650       SCIP_CALL( SCIPhashmapCreate(&varmaps[i], SCIPblkmem(subproblems[i]), nvars) );
5651    }
5652 
5653    /* copying the constraints to the appropriate subproblems */
5654    for( i = 0; i < nconss; i++ )
5655    {
5656       /* we are only interested in the constraints that are in the blocks. These are identified by a label >= 0 */
5657       if( conslabels[i] >= 0 )
5658       {
5659          SCIP_CALL( addConstraintToBendersSubproblem(set, subproblems[conslabels[i]], varmaps[conslabels[i]],
5660                conss[i]) );
5661       }
5662    }
5663 
5664    /* removing the variables and constraints from the master problem that have been added to the subproblem */
5665    SCIP_CALL( removeVariablesAndConstraintsFromMaster(set->scip, conss, vars, conslabels, varslabels, nconss, nvars) );
5666 
5667    /* creating the Benders' decomposition my calling the default plugin */
5668    SCIP_CALL( SCIPcreateBendersDefault(set->scip, subproblems, nblocks) );
5669 
5670    /* flag to the Benders' decomposition core that the subproblems need to be freed */
5671    benders->freesubprobs = TRUE;
5672 
5673    /* activating the Benders' constraint handler for the scenario stages.
5674     * TODO: consider whether the two-phase method should be activated by default in the scenario stages.
5675     */
5676    SCIP_CALL( SCIPsetBoolParam(set->scip, "constraints/benders/active", TRUE) );
5677 
5678    /* changing settings that are required for Benders' decomposition */
5679    SCIP_CALL( SCIPsetPresolving(set->scip, SCIP_PARAMSETTING_OFF, TRUE) );
5680    SCIP_CALL( SCIPsetIntParam(set->scip, "propagating/maxrounds", 0) );
5681    SCIP_CALL( SCIPsetIntParam(set->scip, "propagating/maxroundsroot", 0) );
5682    SCIP_CALL( SCIPsetIntParam(set->scip, "heuristics/trysol/freq", 1) );
5683 
5684    /* disabling aggregation since it can affect the mapping between the master and subproblem variables */
5685    SCIP_CALL( SCIPsetBoolParam(set->scip, "presolving/donotaggr", TRUE) );
5686    SCIP_CALL( SCIPsetBoolParam(set->scip, "presolving/donotmultaggr", TRUE) );
5687 
5688    /* freeing the allocated memory */
5689    for( i = nblocks - 1; i >= 0; i-- )
5690    {
5691       SCIPhashmapFree(&varmaps[i]);
5692    }
5693 
5694    SCIPfreeBufferArray(set->scip, &varmaps);
5695    SCIPfreeBufferArray(set->scip, &conslabels);
5696    SCIPfreeBufferArray(set->scip, &varslabels);
5697    SCIPfreeBufferArray(set->scip, &subproblems);
5698 
5699    return SCIP_OKAY;
5700 }
5701 
5702 /** Returns the corresponding master or subproblem variable for the given variable.
5703  *  This provides a call back for the variable mapping between the master and subproblems. */
SCIPbendersGetVar(SCIP_BENDERS * benders,SCIP_SET * set,SCIP_VAR * var,SCIP_VAR ** mappedvar,int probnumber)5704 SCIP_RETCODE SCIPbendersGetVar(
5705    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
5706    SCIP_SET*             set,                /**< global SCIP settings */
5707    SCIP_VAR*             var,                /**< the variable for which the corresponding variable is desired */
5708    SCIP_VAR**            mappedvar,          /**< the variable that is mapped to var */
5709    int                   probnumber          /**< the problem number for the desired variable, -1 for the master problem */
5710    )
5711 {
5712    assert(benders != NULL);
5713    assert(set != NULL);
5714    assert(var != NULL);
5715    assert(mappedvar != NULL);
5716    assert(benders->bendersgetvar != NULL);
5717 
5718    (*mappedvar) = NULL;
5719 
5720    /* if the variable name matches the auxiliary variable, then the master variable is returned as NULL */
5721    if( strstr(SCIPvarGetName(var), AUXILIARYVAR_NAME) != NULL )
5722       return SCIP_OKAY;
5723 
5724    SCIP_CALL( benders->bendersgetvar(set->scip, benders, var, mappedvar, probnumber) );
5725 
5726    return SCIP_OKAY;
5727 }
5728 
5729 /** gets user data of Benders' decomposition */
SCIPbendersGetData(SCIP_BENDERS * benders)5730 SCIP_BENDERSDATA* SCIPbendersGetData(
5731    SCIP_BENDERS*         benders             /**< Benders' decomposition */
5732    )
5733 {
5734    assert(benders != NULL);
5735 
5736    return benders->bendersdata;
5737 }
5738 
5739 /** sets user data of Benders' decomposition; user has to free old data in advance! */
SCIPbendersSetData(SCIP_BENDERS * benders,SCIP_BENDERSDATA * bendersdata)5740 void SCIPbendersSetData(
5741    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
5742    SCIP_BENDERSDATA*     bendersdata         /**< new Benders' decomposition user data */
5743    )
5744 {
5745    assert(benders != NULL);
5746 
5747    benders->bendersdata = bendersdata;
5748 }
5749 
5750 /** sets copy callback of Benders' decomposition */
SCIPbendersSetCopy(SCIP_BENDERS * benders,SCIP_DECL_BENDERSCOPY ((* benderscopy)))5751 void SCIPbendersSetCopy(
5752    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
5753    SCIP_DECL_BENDERSCOPY ((*benderscopy))    /**< copy callback of Benders' decomposition */
5754    )
5755 {
5756    assert(benders != NULL);
5757 
5758    benders->benderscopy = benderscopy;
5759 }
5760 
5761 /** sets destructor callback of Benders' decomposition */
SCIPbendersSetFree(SCIP_BENDERS * benders,SCIP_DECL_BENDERSFREE ((* bendersfree)))5762 void SCIPbendersSetFree(
5763    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
5764    SCIP_DECL_BENDERSFREE ((*bendersfree))    /**< destructor of Benders' decomposition */
5765    )
5766 {
5767    assert(benders != NULL);
5768 
5769    benders->bendersfree = bendersfree;
5770 }
5771 
5772 /** sets initialization callback of Benders' decomposition */
SCIPbendersSetInit(SCIP_BENDERS * benders,SCIP_DECL_BENDERSINIT ((* bendersinit)))5773 void SCIPbendersSetInit(
5774    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
5775    SCIP_DECL_BENDERSINIT((*bendersinit))     /**< initialize the Benders' decomposition */
5776    )
5777 {
5778    assert(benders != NULL);
5779 
5780    benders->bendersinit = bendersinit;
5781 }
5782 
5783 /** sets deinitialization callback of Benders' decomposition */
SCIPbendersSetExit(SCIP_BENDERS * benders,SCIP_DECL_BENDERSEXIT ((* bendersexit)))5784 void SCIPbendersSetExit(
5785    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
5786    SCIP_DECL_BENDERSEXIT((*bendersexit))     /**< deinitialize the Benders' decomposition */
5787    )
5788 {
5789    assert(benders != NULL);
5790 
5791    benders->bendersexit = bendersexit;
5792 }
5793 
5794 /** sets presolving initialization callback of Benders' decomposition */
SCIPbendersSetInitpre(SCIP_BENDERS * benders,SCIP_DECL_BENDERSINITPRE ((* bendersinitpre)))5795 void SCIPbendersSetInitpre(
5796    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
5797    SCIP_DECL_BENDERSINITPRE((*bendersinitpre))/**< initialize presolving for Benders' decomposition */
5798    )
5799 {
5800    assert(benders != NULL);
5801 
5802    benders->bendersinitpre = bendersinitpre;
5803 }
5804 
5805 /** sets presolving deinitialization callback of Benders' decomposition */
SCIPbendersSetExitpre(SCIP_BENDERS * benders,SCIP_DECL_BENDERSEXITPRE ((* bendersexitpre)))5806 void SCIPbendersSetExitpre(
5807    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
5808    SCIP_DECL_BENDERSEXITPRE((*bendersexitpre))/**< deinitialize presolving for Benders' decomposition */
5809    )
5810 {
5811    assert(benders != NULL);
5812 
5813    benders->bendersexitpre = bendersexitpre;
5814 }
5815 
5816 /** sets solving process initialization callback of Benders' decomposition */
SCIPbendersSetInitsol(SCIP_BENDERS * benders,SCIP_DECL_BENDERSINITSOL ((* bendersinitsol)))5817 void SCIPbendersSetInitsol(
5818    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
5819    SCIP_DECL_BENDERSINITSOL((*bendersinitsol))/**< solving process initialization callback of Benders' decomposition */
5820    )
5821 {
5822    assert(benders != NULL);
5823 
5824    benders->bendersinitsol = bendersinitsol;
5825 }
5826 
5827 /** sets solving process deinitialization callback of Benders' decomposition */
SCIPbendersSetExitsol(SCIP_BENDERS * benders,SCIP_DECL_BENDERSEXITSOL ((* bendersexitsol)))5828 void SCIPbendersSetExitsol(
5829    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
5830    SCIP_DECL_BENDERSEXITSOL((*bendersexitsol))/**< solving process deinitialization callback of Benders' decomposition */
5831    )
5832 {
5833    assert(benders != NULL);
5834 
5835    benders->bendersexitsol = bendersexitsol;
5836 }
5837 
5838 /** sets the pre subproblem solve callback of Benders' decomposition */
SCIPbendersSetPresubsolve(SCIP_BENDERS * benders,SCIP_DECL_BENDERSPRESUBSOLVE ((* benderspresubsolve)))5839 void SCIPbendersSetPresubsolve(
5840    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
5841    SCIP_DECL_BENDERSPRESUBSOLVE((*benderspresubsolve))/**< called prior to the subproblem solving loop */
5842    )
5843 {
5844    assert(benders != NULL);
5845 
5846    benders->benderspresubsolve = benderspresubsolve;
5847 }
5848 
5849 /** sets convex solve callback of Benders' decomposition */
SCIPbendersSetSolvesubconvex(SCIP_BENDERS * benders,SCIP_DECL_BENDERSSOLVESUBCONVEX ((* benderssolvesubconvex)))5850 void SCIPbendersSetSolvesubconvex(
5851    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
5852    SCIP_DECL_BENDERSSOLVESUBCONVEX((*benderssolvesubconvex))/**< solving method for the convex Benders' decomposition subproblem */
5853    )
5854 {
5855    assert(benders != NULL);
5856 
5857    benders->benderssolvesubconvex = benderssolvesubconvex;
5858 }
5859 
5860 /** sets solve callback of Benders' decomposition */
SCIPbendersSetSolvesub(SCIP_BENDERS * benders,SCIP_DECL_BENDERSSOLVESUB ((* benderssolvesub)))5861 void SCIPbendersSetSolvesub(
5862    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
5863    SCIP_DECL_BENDERSSOLVESUB((*benderssolvesub))/**< solving method for a Benders' decomposition subproblem */
5864    )
5865 {
5866    assert(benders != NULL);
5867 
5868    benders->benderssolvesub = benderssolvesub;
5869 }
5870 
5871 /** sets post-solve callback of Benders' decomposition */
SCIPbendersSetPostsolve(SCIP_BENDERS * benders,SCIP_DECL_BENDERSPOSTSOLVE ((* benderspostsolve)))5872 void SCIPbendersSetPostsolve(
5873    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
5874    SCIP_DECL_BENDERSPOSTSOLVE((*benderspostsolve))/**< solving process deinitialization callback of Benders' decomposition */
5875    )
5876 {
5877    assert(benders != NULL);
5878 
5879    benders->benderspostsolve = benderspostsolve;
5880 }
5881 
5882 /** sets post-solve callback of Benders' decomposition */
SCIPbendersSetSubproblemComp(SCIP_BENDERS * benders,SCIP_DECL_SORTPTRCOMP ((* benderssubcomp)))5883 void SCIPbendersSetSubproblemComp(
5884    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
5885    SCIP_DECL_SORTPTRCOMP((*benderssubcomp))  /**< a comparator for defining the solving order of the subproblems */
5886    )
5887 {
5888    assert(benders != NULL);
5889 
5890    benders->benderssubcomp = benderssubcomp;
5891 }
5892 
5893 /** sets free subproblem callback of Benders' decomposition */
SCIPbendersSetFreesub(SCIP_BENDERS * benders,SCIP_DECL_BENDERSFREESUB ((* bendersfreesub)))5894 void SCIPbendersSetFreesub(
5895    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
5896    SCIP_DECL_BENDERSFREESUB((*bendersfreesub))/**< the freeing callback for the subproblem */
5897    )
5898 {
5899    assert(benders != NULL);
5900 
5901    benders->bendersfreesub = bendersfreesub;
5902 }
5903 
5904 /** gets name of Benders' decomposition */
SCIPbendersGetName(SCIP_BENDERS * benders)5905 const char* SCIPbendersGetName(
5906    SCIP_BENDERS*         benders             /**< Benders' decomposition */
5907    )
5908 {
5909    assert(benders != NULL);
5910 
5911    return benders->name;
5912 }
5913 
5914 /** gets description of Benders' decomposition */
SCIPbendersGetDesc(SCIP_BENDERS * benders)5915 const char* SCIPbendersGetDesc(
5916    SCIP_BENDERS*         benders             /**< Benders' decomposition */
5917    )
5918 {
5919    assert(benders != NULL);
5920 
5921    return benders->desc;
5922 }
5923 
5924 /** gets priority of Benders' decomposition */
SCIPbendersGetPriority(SCIP_BENDERS * benders)5925 int SCIPbendersGetPriority(
5926    SCIP_BENDERS*         benders             /**< Benders' decomposition */
5927    )
5928 {
5929    assert(benders != NULL);
5930 
5931    return benders->priority;
5932 }
5933 
5934 /** sets priority of Benders' decomposition */
SCIPbendersSetPriority(SCIP_BENDERS * benders,SCIP_SET * set,int priority)5935 void SCIPbendersSetPriority(
5936    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
5937    SCIP_SET*             set,                /**< global SCIP settings */
5938    int                   priority            /**< new priority of the Benders' decomposition */
5939    )
5940 {
5941    assert(benders != NULL);
5942    assert(set != NULL);
5943 
5944    benders->priority = priority;
5945    set->benderssorted = FALSE;
5946 }
5947 
5948 /** gets the number of subproblems for the Benders' decomposition */
SCIPbendersGetNSubproblems(SCIP_BENDERS * benders)5949 int SCIPbendersGetNSubproblems(
5950    SCIP_BENDERS*         benders             /**< the Benders' decomposition data structure */
5951    )
5952 {
5953    assert(benders != NULL);
5954 
5955    return benders->nsubproblems;
5956 }
5957 
5958 /** returns the SCIP instance for a given subproblem */
SCIPbendersSubproblem(SCIP_BENDERS * benders,int probnumber)5959 SCIP* SCIPbendersSubproblem(
5960    SCIP_BENDERS*         benders,            /**< the Benders' decomposition data structure */
5961    int                   probnumber          /**< the subproblem number */
5962    )
5963 {
5964    assert(benders != NULL);
5965    assert(probnumber >= 0 && probnumber < benders->nsubproblems);
5966 
5967    return benders->subproblems[probnumber];
5968 }
5969 
5970 /** gets the number of times, the Benders' decomposition was called and tried to find a variable with negative reduced costs */
SCIPbendersGetNCalls(SCIP_BENDERS * benders)5971 int SCIPbendersGetNCalls(
5972    SCIP_BENDERS*         benders             /**< Benders' decomposition */
5973    )
5974 {
5975    assert(benders != NULL);
5976 
5977    return benders->ncalls;
5978 }
5979 
5980 /** gets the number of optimality cuts found by the collection of Benders' decomposition subproblems */
SCIPbendersGetNCutsFound(SCIP_BENDERS * benders)5981 int SCIPbendersGetNCutsFound(
5982    SCIP_BENDERS*         benders             /**< Benders' decomposition */
5983    )
5984 {
5985    assert(benders != NULL);
5986 
5987    return benders->ncutsfound;
5988 }
5989 
5990 /** gets the number of cuts found from the strengthening round */
SCIPbendersGetNStrengthenCutsFound(SCIP_BENDERS * benders)5991 int SCIPbendersGetNStrengthenCutsFound(
5992    SCIP_BENDERS*         benders             /**< Benders' decomposition */
5993    )
5994 {
5995    assert(benders != NULL);
5996 
5997    return benders->nstrengthencuts;
5998 }
5999 
6000 /** gets the number of calls to the strengthening round */
SCIPbendersGetNStrengthenCalls(SCIP_BENDERS * benders)6001 int SCIPbendersGetNStrengthenCalls(
6002    SCIP_BENDERS*         benders             /**< Benders' decomposition */
6003    )
6004 {
6005    assert(benders != NULL);
6006 
6007    return benders->nstrengthencalls;
6008 }
6009 
6010 /** gets the number of calls to the strengthening round that fail */
SCIPbendersGetNStrengthenFails(SCIP_BENDERS * benders)6011 int SCIPbendersGetNStrengthenFails(
6012    SCIP_BENDERS*         benders             /**< Benders' decomposition */
6013    )
6014 {
6015    assert(benders != NULL);
6016 
6017    return benders->nstrengthenfails;
6018 }
6019 
6020 /** gets time in seconds used in this Benders' decomposition for setting up for next stages */
SCIPbendersGetSetupTime(SCIP_BENDERS * benders)6021 SCIP_Real SCIPbendersGetSetupTime(
6022    SCIP_BENDERS*         benders             /**< Benders' decomposition */
6023    )
6024 {
6025    assert(benders != NULL);
6026 
6027    return SCIPclockGetTime(benders->setuptime);
6028 }
6029 
6030 /** gets time in seconds used in this Benders' decomposition */
SCIPbendersGetTime(SCIP_BENDERS * benders)6031 SCIP_Real SCIPbendersGetTime(
6032    SCIP_BENDERS*         benders             /**< Benders' decomposition */
6033    )
6034 {
6035    assert(benders != NULL);
6036 
6037    return SCIPclockGetTime(benders->bendersclock);
6038 }
6039 
6040 /** enables or disables all clocks of the Benders' decomposition, depending on the value of the flag */
SCIPbendersEnableOrDisableClocks(SCIP_BENDERS * benders,SCIP_Bool enable)6041 void SCIPbendersEnableOrDisableClocks(
6042    SCIP_BENDERS*         benders,            /**< the Benders' decomposition for which all clocks should be enabled or disabled */
6043    SCIP_Bool             enable              /**< should the clocks of the Benders' decomposition be enabled? */
6044    )
6045 {
6046    assert(benders != NULL);
6047 
6048    SCIPclockEnableOrDisable(benders->setuptime, enable);
6049    SCIPclockEnableOrDisable(benders->bendersclock, enable);
6050 }
6051 
6052 /** is Benders' decomposition initialized? */
SCIPbendersIsInitialized(SCIP_BENDERS * benders)6053 SCIP_Bool SCIPbendersIsInitialized(
6054    SCIP_BENDERS*         benders             /**< Benders' decomposition */
6055    )
6056 {
6057    assert(benders != NULL);
6058 
6059    return benders->initialized;
6060 }
6061 
6062 /** Are Benders' cuts generated from the LP solutions? */
SCIPbendersCutLP(SCIP_BENDERS * benders)6063 SCIP_Bool SCIPbendersCutLP(
6064    SCIP_BENDERS*         benders             /**< Benders' decomposition */
6065    )
6066 {
6067    assert(benders != NULL);
6068 
6069    return benders->cutlp;
6070 }
6071 
6072 /** Are Benders' cuts generated from the pseudo solutions? */
SCIPbendersCutPseudo(SCIP_BENDERS * benders)6073 SCIP_Bool SCIPbendersCutPseudo(
6074    SCIP_BENDERS*         benders             /**< Benders' decomposition */
6075    )
6076 {
6077    assert(benders != NULL);
6078 
6079    return benders->cutpseudo;
6080 }
6081 
6082 /** Are Benders' cuts generated from the relaxation solutions? */
SCIPbendersCutRelaxation(SCIP_BENDERS * benders)6083 SCIP_Bool SCIPbendersCutRelaxation(
6084    SCIP_BENDERS*         benders             /**< Benders' decomposition */
6085    )
6086 {
6087    assert(benders != NULL);
6088 
6089    return benders->cutrelax;
6090 }
6091 
6092 /** should this Benders' use the auxiliary variables from the highest priority Benders' */
SCIPbendersShareAuxVars(SCIP_BENDERS * benders)6093 SCIP_Bool SCIPbendersShareAuxVars(
6094    SCIP_BENDERS*         benders             /**< Benders' decomposition */
6095    )
6096 {
6097    assert(benders != NULL);
6098 
6099    return benders->shareauxvars;
6100 }
6101 
6102 /** adds a subproblem to the Benders' decomposition data. If a custom subproblem solving method is used, then the
6103  *  subproblem pointer can be set to NULL
6104  */
SCIPbendersAddSubproblem(SCIP_BENDERS * benders,SCIP * subproblem)6105 SCIP_RETCODE SCIPbendersAddSubproblem(
6106    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
6107    SCIP*                 subproblem          /**< subproblem to be added to the data storage, can be NULL */
6108    )
6109 {
6110    assert(benders != NULL);
6111    assert(benders->naddedsubprobs + 1 <= benders->nsubproblems);
6112 
6113    /* if the subproblem pointer is NULL, then the subproblem solving callback functions must be set. */
6114    if( subproblem == NULL && (!benders->benderssolvesubconvex || !benders->benderssolvesub) )
6115    {
6116       SCIPerrorMessage("The subproblem can only be set to NULL if both bendersSolvesubconvex%s and bendersSolvesub%s "
6117          "are defined.\n", benders->name, benders->name);
6118       return SCIP_ERROR;
6119    }
6120 
6121    benders->subproblems[benders->naddedsubprobs] = subproblem;
6122 
6123    benders->naddedsubprobs++;
6124 
6125    return SCIP_OKAY;
6126 }
6127 
6128 /** removes the subproblems from the Benders' decomposition data */
SCIPbendersRemoveSubproblems(SCIP_BENDERS * benders)6129 void SCIPbendersRemoveSubproblems(
6130    SCIP_BENDERS*         benders             /**< Benders' decomposition */
6131    )
6132 {
6133    assert(benders != NULL);
6134    assert(benders->subproblems != NULL);
6135 
6136    BMSclearMemoryArray(&benders->subproblems, benders->naddedsubprobs);
6137    benders->naddedsubprobs = 0;
6138 }
6139 
6140 /** returns the auxiliary variable for the given subproblem */
SCIPbendersGetAuxiliaryVar(SCIP_BENDERS * benders,int probnumber)6141 SCIP_VAR* SCIPbendersGetAuxiliaryVar(
6142    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
6143    int                   probnumber          /**< the subproblem number */
6144    )
6145 {
6146    assert(benders != NULL);
6147    assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders));
6148 
6149    return benders->auxiliaryvars[probnumber];
6150 }
6151 
6152 /** returns all auxiliary variables */
SCIPbendersGetAuxiliaryVars(SCIP_BENDERS * benders)6153 SCIP_VAR** SCIPbendersGetAuxiliaryVars(
6154    SCIP_BENDERS*         benders             /**< Benders' decomposition */
6155    )
6156 {
6157    assert(benders != NULL);
6158 
6159    return benders->auxiliaryvars;
6160 }
6161 
6162 /** stores the objective function value of the subproblem for use in cut generation */
SCIPbendersSetSubproblemObjval(SCIP_BENDERS * benders,int probnumber,SCIP_Real objval)6163 void SCIPbendersSetSubproblemObjval(
6164    SCIP_BENDERS*         benders,            /**< the Benders' decomposition structure */
6165    int                   probnumber,         /**< the subproblem number */
6166    SCIP_Real             objval              /**< the objective function value for the subproblem */
6167    )
6168 {
6169    assert(benders != NULL);
6170    assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders));
6171 
6172    /* updating the best objval */
6173    if( objval < benders->bestsubprobobjval[probnumber] )
6174       benders->bestsubprobobjval[probnumber] = objval;
6175 
6176    benders->subprobobjval[probnumber] = objval;
6177 }
6178 
6179 /** returns the objective function value of the subproblem for use in cut generation */
SCIPbendersGetSubproblemObjval(SCIP_BENDERS * benders,int probnumber)6180 SCIP_Real SCIPbendersGetSubproblemObjval(
6181    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
6182    int                   probnumber          /**< the subproblem number */
6183    )
6184 {
6185    assert(benders != NULL);
6186    assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders));
6187 
6188    return benders->subprobobjval[probnumber];
6189 }
6190 
6191 /** returns whether the solution has non-zero slack variables */
SCIPbendersSolSlackVarsActive(SCIP_BENDERS * benders,SCIP_Bool * activeslack)6192 SCIP_RETCODE SCIPbendersSolSlackVarsActive(
6193    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
6194    SCIP_Bool*            activeslack         /**< flag to indicate whether a slack variable is active */
6195    )
6196 {
6197    SCIP* subproblem;
6198    SCIP_SOL* sol;
6199    SCIP_VAR** vars;
6200    int nsubproblems;
6201    int nvars;
6202    int ncontvars;
6203    int i;
6204    int j;
6205    SCIP_Bool freesol = FALSE;
6206 
6207    assert(benders != NULL);
6208    assert(activeslack != NULL);
6209 
6210    (*activeslack) = FALSE;
6211 
6212    /* if the slack variables have not been added, then we can immediately state that no slack variables are active */
6213    if( !benders->feasibilityphase )
6214    {
6215       return SCIP_OKAY;
6216    }
6217 
6218    nsubproblems = SCIPbendersGetNSubproblems(benders);
6219 
6220    /* checking all subproblems for active slack variables */
6221    for( i = 0; i < nsubproblems && !(*activeslack); i++ )
6222    {
6223       subproblem = SCIPbendersSubproblem(benders, i);
6224 
6225       /* if the subproblem is convex and an NLP, then we need to create the NLP solution. Otherwise, the solution can be
6226        * retrieved from the LP or CIP.
6227        */
6228       if( SCIPbendersGetSubproblemType(benders, i) == SCIP_BENDERSSUBTYPE_CONVEXCONT )
6229       {
6230          if( SCIPisNLPConstructed(subproblem) && SCIPgetNNlpis(subproblem) > 0 )
6231          {
6232             SCIP_CALL( SCIPcreateNLPSol(subproblem, &sol, NULL) );
6233          }
6234          else
6235          {
6236             SCIP_CALL( SCIPcreateCurrentSol(subproblem, &sol, NULL) );
6237          }
6238          freesol = TRUE;
6239       }
6240       else
6241          sol = SCIPgetBestSol(subproblem);
6242 
6243       /* getting the variable data. Only the continuous variables are important. */
6244       SCIP_CALL( SCIPgetVarsData(subproblem, &vars, &nvars, NULL, NULL, NULL, &ncontvars) );
6245 
6246       /* checking all slack variables for non-zero solution values */
6247       for( j = nvars - 1; j >= nvars - ncontvars; j-- )
6248       {
6249          if( strstr(SCIPvarGetName(vars[j]), SLACKVAR_NAME) != NULL )
6250          {
6251             if( !SCIPisZero(subproblem, SCIPgetSolVal(subproblem, sol, vars[j])) )
6252             {
6253                (*activeslack) = TRUE;
6254                break;
6255             }
6256          }
6257       }
6258 
6259       /* freeing the LP and NLP solutions */
6260       if( freesol )
6261       {
6262          SCIP_CALL( SCIPfreeSol(subproblem, &sol) );
6263       }
6264    }
6265 
6266    return SCIP_OKAY;
6267 }
6268 
6269 /** sets the subproblem type
6270  *
6271  * The subproblem types are:
6272  *    - Convex constraints with continuous variables
6273  *    - Convex constraints with discrete variables
6274  *    - Non-convex constraints with continuous variables
6275  *    - Non-convex constraints with discrete variables
6276  */
SCIPbendersSetSubproblemType(SCIP_BENDERS * benders,int probnumber,SCIP_BENDERSSUBTYPE subprobtype)6277 void SCIPbendersSetSubproblemType(
6278    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
6279    int                   probnumber,         /**< the subproblem number */
6280    SCIP_BENDERSSUBTYPE   subprobtype         /**< the subproblem type */
6281    )
6282 {
6283    assert(benders != NULL);
6284    assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders));
6285 
6286    if( subprobtype == SCIP_BENDERSSUBTYPE_CONVEXCONT
6287       && benders->subprobtype[probnumber] != SCIP_BENDERSSUBTYPE_CONVEXCONT )
6288       benders->nconvexsubprobs++;
6289    else if( subprobtype != SCIP_BENDERSSUBTYPE_CONVEXCONT
6290       && benders->subprobtype[probnumber] == SCIP_BENDERSSUBTYPE_CONVEXCONT )
6291       benders->nconvexsubprobs--;
6292 
6293    benders->subprobtype[probnumber] = subprobtype;
6294 
6295    assert(benders->nconvexsubprobs >= 0 && benders->nconvexsubprobs <= benders->nsubproblems);
6296 }
6297 
6298 /** returns the type of the subproblem
6299  *
6300  *  This type is used to determine whether the duals of the problem can be used to generate cuts
6301  */
SCIPbendersGetSubproblemType(SCIP_BENDERS * benders,int probnumber)6302 SCIP_BENDERSSUBTYPE SCIPbendersGetSubproblemType(
6303    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
6304    int                   probnumber          /**< the subproblem number */
6305    )
6306 {
6307    assert(benders != NULL);
6308    assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders));
6309 
6310    return benders->subprobtype[probnumber];
6311 }
6312 
6313 /** sets the flag indicating whether a subproblem is convex
6314  *
6315  *  It is possible that this can change during the solving process. One example is when the three-phase method is
6316  *  employed, where the first phase solves the convex relaxation of both the master and subproblems, the second phase
6317  *  reintroduces the integrality constraints to the master problem and the third phase then reintroduces integrality
6318  *  constraints to the subproblems.
6319  */
SCIPbendersSetSubproblemIsConvex(SCIP_BENDERS * benders,int probnumber,SCIP_Bool isconvex)6320 void SCIPbendersSetSubproblemIsConvex(
6321    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
6322    int                   probnumber,         /**< the subproblem number */
6323    SCIP_Bool             isconvex            /**< flag to indicate whether the subproblem is convex */
6324    )
6325 {
6326    assert(benders != NULL);
6327    assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders));
6328 
6329    if( isconvex && !benders->subprobisconvex[probnumber] )
6330       benders->nconvexsubprobs++;
6331    else if( !isconvex && benders->subprobisconvex[probnumber] )
6332       benders->nconvexsubprobs--;
6333 
6334    benders->subprobisconvex[probnumber] = isconvex;
6335 
6336    assert(benders->nconvexsubprobs >= 0 && benders->nconvexsubprobs <= benders->nsubproblems);
6337 }
6338 
6339 /** returns whether the subproblem is convex
6340  *
6341  *  This means that the dual solution can be used to generate cuts.
6342  */
SCIPbendersSubproblemIsConvex(SCIP_BENDERS * benders,int probnumber)6343 SCIP_Bool SCIPbendersSubproblemIsConvex(
6344    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
6345    int                   probnumber          /**< the subproblem number */
6346    )
6347 {
6348    assert(benders != NULL);
6349    assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders));
6350 
6351    return benders->subprobisconvex[probnumber];
6352 }
6353 
6354 /** returns the number of subproblems that are convex */
SCIPbendersGetNConvexSubproblems(SCIP_BENDERS * benders)6355 int SCIPbendersGetNConvexSubproblems(
6356    SCIP_BENDERS*         benders             /**< Benders' decomposition */
6357    )
6358 {
6359    assert(benders != NULL);
6360 
6361    return benders->nconvexsubprobs;
6362 }
6363 
6364 /** sets the flag indicating whether a subproblem contains non-linear constraints */
SCIPbendersSetSubproblemIsNonlinear(SCIP_BENDERS * benders,int probnumber,SCIP_Bool isnonlinear)6365 void SCIPbendersSetSubproblemIsNonlinear(
6366    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
6367    int                   probnumber,         /**< the subproblem number */
6368    SCIP_Bool             isnonlinear         /**< flag to indicate whether the subproblem contains non-linear constraints */
6369    )
6370 {
6371    assert(benders != NULL);
6372    assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders));
6373 
6374    if( isnonlinear && !benders->subprobisnonlinear[probnumber] )
6375       benders->nnonlinearsubprobs++;
6376    else if( !isnonlinear && benders->subprobisnonlinear[probnumber] )
6377       benders->nnonlinearsubprobs--;
6378 
6379    benders->subprobisnonlinear[probnumber] = isnonlinear;
6380 
6381    assert(benders->nnonlinearsubprobs >= 0 && benders->nnonlinearsubprobs <= benders->nsubproblems);
6382 }
6383 
6384 /** returns whether the subproblem contains non-linear constraints. */
SCIPbendersSubproblemIsNonlinear(SCIP_BENDERS * benders,int probnumber)6385 SCIP_Bool SCIPbendersSubproblemIsNonlinear(
6386    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
6387    int                   probnumber          /**< the subproblem number */
6388    )
6389 {
6390    assert(benders != NULL);
6391    assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders));
6392 
6393    return benders->subprobisnonlinear[probnumber];
6394 }
6395 
6396 /** returns the number of subproblems that contain non-linear constraints  */
SCIPbendersGetNNonlinearSubproblems(SCIP_BENDERS * benders)6397 int SCIPbendersGetNNonlinearSubproblems(
6398    SCIP_BENDERS*         benders             /**< Benders' decomposition */
6399    )
6400 {
6401    assert(benders != NULL);
6402 
6403    return benders->nnonlinearsubprobs;
6404 }
6405 
6406 /** sets the flag indicating whether the master problem contains non-linear constraints */
SCIPbendersSetMasterIsNonlinear(SCIP_BENDERS * benders,SCIP_Bool isnonlinear)6407 void SCIPbendersSetMasterIsNonlinear(
6408    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
6409    SCIP_Bool             isnonlinear         /**< flag to indicate whether the subproblem contains non-linear constraints */
6410    )
6411 {
6412    assert(benders != NULL);
6413 
6414    benders->masterisnonlinear = isnonlinear;
6415 }
6416 
6417 /** returns whether the master problem contains non-linear constraints. */
SCIPbendersMasterIsNonlinear(SCIP_BENDERS * benders)6418 SCIP_Bool SCIPbendersMasterIsNonlinear(
6419    SCIP_BENDERS*         benders             /**< Benders' decomposition */
6420    )
6421 {
6422    assert(benders != NULL);
6423 
6424    return benders->masterisnonlinear;
6425 }
6426 
6427 /** returns the flag indicating that Benders' decomposition is in a cut strengthening round */
SCIPbendersInStrengthenRound(SCIP_BENDERS * benders)6428 SCIP_Bool SCIPbendersInStrengthenRound(
6429    SCIP_BENDERS*         benders             /**< Benders' decomposition */
6430    )
6431 {
6432    assert(benders != NULL);
6433 
6434    return benders->strengthenround;
6435 }
6436 
6437 /** changes all of the master problem variables in the given subproblem to continuous. */
SCIPbendersChgMastervarsToCont(SCIP_BENDERS * benders,SCIP_SET * set,int probnumber)6438 SCIP_RETCODE SCIPbendersChgMastervarsToCont(
6439    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
6440    SCIP_SET*             set,                /**< global SCIP settings */
6441    int                   probnumber          /**< the subproblem number */
6442    )
6443 {
6444    SCIP* subproblem;
6445    SCIP_VAR** vars;
6446    int nbinvars;
6447    int nintvars;
6448    int nimplvars;
6449    int chgvarscount;
6450    int origintvars;
6451    int i;
6452    SCIP_Bool infeasible;
6453 
6454    assert(benders != NULL);
6455    assert(set != NULL);
6456    assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders));
6457 
6458    subproblem = SCIPbendersSubproblem(benders, probnumber);
6459    assert(subproblem != NULL);
6460 
6461    /* only set the master problem variable to continuous if they have not already been changed. */
6462    if( !SCIPbendersGetMastervarsCont(benders, probnumber) )
6463    {
6464       SCIP_VAR* mastervar;
6465 
6466       /* retrieving the variable data */
6467       SCIP_CALL( SCIPgetVarsData(subproblem, &vars, NULL, &nbinvars, &nintvars, &nimplvars, NULL) );
6468 
6469       origintvars = nbinvars + nintvars + nimplvars;
6470 
6471       chgvarscount = 0;
6472 
6473       /* looping over all integer variables to change the master variables to continuous */
6474       i = 0;
6475       while( i < nbinvars + nintvars + nimplvars )
6476       {
6477          SCIP_CALL( SCIPbendersGetVar(benders, set, vars[i], &mastervar, -1) );
6478 
6479          if( SCIPvarGetType(vars[i]) != SCIP_VARTYPE_CONTINUOUS && mastervar != NULL )
6480          {
6481             /* changing the type of the subproblem variable corresponding to mastervar to CONTINUOUS */
6482             SCIP_CALL( SCIPchgVarType(subproblem, vars[i], SCIP_VARTYPE_CONTINUOUS, &infeasible) );
6483 
6484             assert(!infeasible);
6485 
6486             chgvarscount++;
6487             SCIP_CALL( SCIPgetVarsData(subproblem, NULL, NULL, &nbinvars, &nintvars, &nimplvars, NULL) );
6488          }
6489          else
6490             i++;
6491       }
6492 
6493       /* if all of the integer variables have been changed to continuous, then the subproblem could now be a convex
6494        * problem. This must be checked and if TRUE, then the LP subproblem is initialised and then put into probing
6495        * mode
6496        */
6497       if( chgvarscount > 0 && chgvarscount == origintvars )
6498       {
6499          /* checking the convexity of the subproblem */
6500          SCIP_CALL( checkSubproblemConvexity(benders, set, probnumber) );
6501 
6502          /* if the subproblem has convex constraints and continuous variables, then it is initialised and put into
6503           * probing mode
6504           */
6505          if( SCIPbendersGetSubproblemType(benders, probnumber) == SCIP_BENDERSSUBTYPE_CONVEXCONT )
6506          {
6507             SCIP_CALL( initialiseLPSubproblem(benders, set, probnumber) );
6508          }
6509       }
6510 
6511       SCIP_CALL( SCIPbendersSetMastervarsCont(benders, probnumber, TRUE) );
6512    }
6513 
6514    return SCIP_OKAY;
6515 }
6516 
6517 /** sets the subproblem setup flag */
SCIPbendersSetSubproblemIsSetup(SCIP_BENDERS * benders,int probnumber,SCIP_Bool issetup)6518 void SCIPbendersSetSubproblemIsSetup(
6519    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
6520    int                   probnumber,         /**< the subproblem number */
6521    SCIP_Bool             issetup             /**< flag to indicate whether the subproblem has been setup */
6522    )
6523 {
6524    assert(benders != NULL);
6525    assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders));
6526 
6527    benders->subprobsetup[probnumber] = issetup;
6528 }
6529 
6530 /** returns the subproblem setup flag */
SCIPbendersSubproblemIsSetup(SCIP_BENDERS * benders,int probnumber)6531 SCIP_Bool SCIPbendersSubproblemIsSetup(
6532    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
6533    int                   probnumber          /**< the subproblem number */
6534    )
6535 {
6536    assert(benders != NULL);
6537    assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders));
6538 
6539    return benders->subprobsetup[probnumber];
6540 }
6541 
6542 /** sets the independent subproblem flag */
SCIPbendersSetSubproblemIsIndependent(SCIP_BENDERS * benders,int probnumber,SCIP_Bool isindep)6543 void SCIPbendersSetSubproblemIsIndependent(
6544    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
6545    int                   probnumber,         /**< the subproblem number */
6546    SCIP_Bool             isindep             /**< flag to indicate whether the subproblem is independent */
6547    )
6548 {
6549    assert(benders != NULL);
6550    assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders));
6551 
6552    /* if the user has defined solving or freeing functions, then it is not possible to declare a subproblem as
6553     * independent. This is because declaring a subproblem as independent changes the solving loop, so it would change
6554     * the expected behaviour of the user defined plugin. If a user calls this function, then an error will be returned.
6555     */
6556    if( benders->benderssolvesubconvex != NULL || benders->benderssolvesub != NULL || benders->bendersfreesub != NULL )
6557    {
6558       SCIPerrorMessage("The user has defined either bendersSolvesubconvex%s, bendersSolvesub%s or bendersFreesub%s. "
6559          "Thus, it is not possible to declare the independence of a subproblem.\n", benders->name, benders->name,
6560          benders->name);
6561       SCIPABORT();
6562    }
6563    else
6564    {
6565       SCIP_Bool activesubprob;
6566 
6567       /* if the active status of the subproblem changes, then we must update the activesubprobs counter */
6568       activesubprob = subproblemIsActive(benders, probnumber);
6569 
6570       benders->indepsubprob[probnumber] = isindep;
6571 
6572       /* updating the activesubprobs counter */
6573       if( activesubprob && !subproblemIsActive(benders, probnumber) )
6574          benders->nactivesubprobs--;
6575       else if( !activesubprob && subproblemIsActive(benders, probnumber) )
6576          benders->nactivesubprobs++;
6577 
6578       assert(benders->nactivesubprobs >= 0 && benders->nactivesubprobs <= SCIPbendersGetNSubproblems(benders));
6579    }
6580 }
6581 
6582 /** returns whether the subproblem is independent */
SCIPbendersSubproblemIsIndependent(SCIP_BENDERS * benders,int probnumber)6583 SCIP_Bool SCIPbendersSubproblemIsIndependent(
6584    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
6585    int                   probnumber          /**< the subproblem number */
6586    )
6587 {
6588    assert(benders != NULL);
6589    assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders));
6590 
6591    return benders->indepsubprob[probnumber];
6592 }
6593 
6594 /** Sets whether the subproblem is enabled or disabled. A subproblem is disabled if it has been merged into the master
6595  *  problem.
6596  */
SCIPbendersSetSubproblemEnabled(SCIP_BENDERS * benders,int probnumber,SCIP_Bool enabled)6597 void SCIPbendersSetSubproblemEnabled(
6598    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
6599    int                   probnumber,         /**< the subproblem number */
6600    SCIP_Bool             enabled             /**< flag to indicate whether the subproblem is enabled */
6601    )
6602 {
6603    SCIP_Bool activesubprob;
6604 
6605    assert(benders != NULL);
6606    assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders));
6607 
6608    /* if the active status of the subproblem changes, then we must update the activesubprobs counter */
6609    activesubprob = subproblemIsActive(benders, probnumber);
6610 
6611    benders->subprobenabled[probnumber] = enabled;
6612 
6613    /* updating the activesubprobs counter */
6614    if( activesubprob && !subproblemIsActive(benders, probnumber) )
6615       benders->nactivesubprobs--;
6616    else if( !activesubprob && subproblemIsActive(benders, probnumber) )
6617       benders->nactivesubprobs++;
6618 
6619    assert(benders->nactivesubprobs >= 0 && benders->nactivesubprobs <= SCIPbendersGetNSubproblems(benders));
6620 }
6621 
6622 /** returns whether the subproblem is enabled, i.e. the subproblem is still solved in the solving loop. */
SCIPbendersSubproblemIsEnabled(SCIP_BENDERS * benders,int probnumber)6623 SCIP_Bool SCIPbendersSubproblemIsEnabled(
6624    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
6625    int                   probnumber          /**< the subproblem number */
6626    )
6627 {
6628    assert(benders != NULL);
6629    assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders));
6630 
6631    return benders->subprobenabled[probnumber];
6632 }
6633 
6634 /** sets a flag to indicate whether the master variables are all set to continuous */
SCIPbendersSetMastervarsCont(SCIP_BENDERS * benders,int probnumber,SCIP_Bool arecont)6635 SCIP_RETCODE SCIPbendersSetMastervarsCont(
6636    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
6637    int                   probnumber,         /**< the subproblem number */
6638    SCIP_Bool             arecont             /**< flag to indicate whether the master problem variables are continuous */
6639    )
6640 {
6641    assert(benders != NULL);
6642    assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders));
6643 
6644    /* if the master variables were all continuous and now are not, then the subproblem must exit probing mode and be
6645     * changed to non-LP subproblem */
6646    if( benders->mastervarscont[probnumber] && !arecont )
6647    {
6648       SCIP_BENDERSSUBTYPE subtype;
6649 
6650       if( SCIPinProbing(SCIPbendersSubproblem(benders, probnumber)) )
6651       {
6652          SCIP_CALL( SCIPendProbing(SCIPbendersSubproblem(benders, probnumber)) );
6653       }
6654 
6655       subtype = SCIPbendersGetSubproblemType(benders, probnumber);
6656       assert(subtype == SCIP_BENDERSSUBTYPE_CONVEXCONT || subtype == SCIP_BENDERSSUBTYPE_NONCONVEXCONT);
6657 
6658       if( subtype == SCIP_BENDERSSUBTYPE_CONVEXCONT )
6659          SCIPbendersSetSubproblemType(benders, probnumber, SCIP_BENDERSSUBTYPE_CONVEXDIS);
6660       else if( subtype == SCIP_BENDERSSUBTYPE_NONCONVEXCONT )
6661          SCIPbendersSetSubproblemType(benders, probnumber, SCIP_BENDERSSUBTYPE_NONCONVEXDIS);
6662    }
6663 
6664    benders->mastervarscont[probnumber] = arecont;
6665 
6666    return SCIP_OKAY;
6667 }
6668 
6669 /** returns whether the master variables are all set to continuous */
SCIPbendersGetMastervarsCont(SCIP_BENDERS * benders,int probnumber)6670 SCIP_Bool SCIPbendersGetMastervarsCont(
6671    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
6672    int                   probnumber          /**< the subproblem number */
6673    )
6674 {
6675    assert(benders != NULL);
6676    assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders));
6677 
6678    return benders->mastervarscont[probnumber];
6679 }
6680 
6681 /** returns the number of cuts that have been transferred from sub SCIPs to the master SCIP */
SCIPbendersGetNTransferredCuts(SCIP_BENDERS * benders)6682 int SCIPbendersGetNTransferredCuts(
6683    SCIP_BENDERS*         benders             /**< the Benders' decomposition data structure */
6684    )
6685 {
6686    assert(benders != NULL);
6687 
6688    return benders->ntransferred;
6689 }
6690 
6691 /** updates the lower bound for the subproblem. If the lower bound is not greater than the previously stored lowerbound,
6692  *  then no update occurs.
6693  */
SCIPbendersUpdateSubproblemLowerbound(SCIP_BENDERS * benders,int probnumber,SCIP_Real lowerbound)6694 void SCIPbendersUpdateSubproblemLowerbound(
6695    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
6696    int                   probnumber,         /**< the subproblem number */
6697    SCIP_Real             lowerbound          /**< the lower bound */
6698    )
6699 {
6700    assert(benders != NULL);
6701    assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders));
6702 
6703    if( EPSGE(lowerbound, benders->subproblowerbound[probnumber], 1e-06) )
6704       benders->subproblowerbound[probnumber] = lowerbound;
6705    else
6706    {
6707       SCIPdebugMessage("The lowerbound %g for subproblem %d is less than the currently stored lower bound %g\n",
6708          lowerbound, probnumber, benders->subproblowerbound[probnumber]);
6709    }
6710 }
6711 
6712 /** returns the stored lower bound for the given subproblem */
SCIPbendersGetSubproblemLowerbound(SCIP_BENDERS * benders,int probnumber)6713 SCIP_Real SCIPbendersGetSubproblemLowerbound(
6714    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
6715    int                   probnumber          /**< the subproblem number */
6716    )
6717 {
6718    assert(benders != NULL);
6719    assert(probnumber >= 0 && probnumber < SCIPbendersGetNSubproblems(benders));
6720 
6721    return benders->subproblowerbound[probnumber];
6722 }
6723 
6724 /** returns the number of cuts that have been added for storage */
SCIPbendersGetNStoredCuts(SCIP_BENDERS * benders)6725 int SCIPbendersGetNStoredCuts(
6726    SCIP_BENDERS*         benders             /**< Benders' decomposition */
6727    )
6728 {
6729    assert(benders != NULL);
6730 
6731    return benders->nstoredcuts;
6732 }
6733 
6734 /** returns the cuts that have been stored for transfer */
SCIPbendersGetStoredCutData(SCIP_BENDERS * benders,int cutidx,SCIP_VAR *** vars,SCIP_Real ** vals,SCIP_Real * lhs,SCIP_Real * rhs,int * nvars)6735 SCIP_RETCODE SCIPbendersGetStoredCutData(
6736    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
6737    int                   cutidx,             /**< the index for the cut data that is requested */
6738    SCIP_VAR***           vars,               /**< the variables that have non-zero coefficients in the cut */
6739    SCIP_Real**           vals,               /**< the coefficients of the variables in the cut */
6740    SCIP_Real*            lhs,                /**< the left hand side of the cut */
6741    SCIP_Real*            rhs,                /**< the right hand side of the cut */
6742    int*                  nvars               /**< the number of variables with non-zero coefficients in the cut */
6743    )
6744 {
6745    assert(benders != NULL);
6746    assert(vars != NULL);
6747    assert(vals != NULL);
6748    assert(lhs != NULL);
6749    assert(rhs != NULL);
6750    assert(nvars != NULL);
6751    assert(cutidx >= 0 && cutidx < benders->nstoredcuts);
6752 
6753    (*vars) = benders->storedcuts[cutidx]->vars;
6754    (*vals) = benders->storedcuts[cutidx]->vals;
6755    (*lhs) = benders->storedcuts[cutidx]->lhs;
6756    (*rhs) = benders->storedcuts[cutidx]->rhs;
6757    (*nvars) = benders->storedcuts[cutidx]->nvars;
6758 
6759    return SCIP_OKAY;
6760 }
6761 
6762 /** returns the original problem data for the cuts that have been added by the Benders' cut plugin. The stored
6763  *  variables and values will populate the input vars and vals arrays. Thus, memory must be allocated for the vars and
6764  *  vals arrays
6765  */
SCIPbendersGetStoredCutOrigData(SCIP_BENDERS * benders,int cutidx,SCIP_VAR *** vars,SCIP_Real ** vals,SCIP_Real * lhs,SCIP_Real * rhs,int * nvars,int varssize)6766 SCIP_RETCODE SCIPbendersGetStoredCutOrigData(
6767    SCIP_BENDERS*         benders,            /**< Benders' decomposition cut */
6768    int                   cutidx,             /**< the index for the cut data that is requested */
6769    SCIP_VAR***           vars,               /**< the variables that have non-zero coefficients in the cut */
6770    SCIP_Real**           vals,               /**< the coefficients of the variables in the cut */
6771    SCIP_Real*            lhs,                /**< the left hand side of the cut */
6772    SCIP_Real*            rhs,                /**< the right hand side of the cut */
6773    int*                  nvars,              /**< the number of variables with non-zero coefficients in the cut */
6774    int                   varssize            /**< the available slots in the array */
6775    )
6776 {
6777    SCIP_VAR* origvar;
6778    SCIP_Real scalar;
6779    SCIP_Real constant;
6780    int i;
6781 
6782    assert(benders != NULL);
6783    assert(vars != NULL);
6784    assert(vals != NULL);
6785    assert(lhs != NULL);
6786    assert(rhs != NULL);
6787    assert(nvars != NULL);
6788    assert(cutidx >= 0 && cutidx < benders->nstoredcuts);
6789 
6790    (*lhs) = benders->storedcuts[cutidx]->lhs;
6791    (*rhs) = benders->storedcuts[cutidx]->rhs;
6792    (*nvars) = benders->storedcuts[cutidx]->nvars;
6793 
6794    /* if there are enough slots, then store the cut variables and values */
6795    if( varssize >= *nvars )
6796    {
6797       for( i = 0; i < *nvars; i++ )
6798       {
6799          /* getting the original variable for the transformed variable */
6800          origvar = benders->storedcuts[cutidx]->vars[i];
6801          scalar = 1.0;
6802          constant = 0.0;
6803          SCIP_CALL( SCIPvarGetOrigvarSum(&origvar, &scalar, &constant) );
6804 
6805          (*vars)[i] = origvar;
6806          (*vals)[i] = benders->storedcuts[cutidx]->vals[i];
6807       }
6808    }
6809 
6810    return SCIP_OKAY;
6811 }
6812 
6813 /** adds the data for the generated cuts to the Benders' cut storage */
SCIPbendersStoreCut(SCIP_BENDERS * benders,SCIP_SET * set,SCIP_VAR ** vars,SCIP_Real * vals,SCIP_Real lhs,SCIP_Real rhs,int nvars)6814 SCIP_RETCODE SCIPbendersStoreCut(
6815    SCIP_BENDERS*         benders,            /**< Benders' decomposition cut */
6816    SCIP_SET*             set,                /**< global SCIP settings */
6817    SCIP_VAR**            vars,               /**< the variables that have non-zero coefficients in the cut */
6818    SCIP_Real*            vals,               /**< the coefficients of the variables in the cut */
6819    SCIP_Real             lhs,                /**< the left hand side of the cut */
6820    SCIP_Real             rhs,                /**< the right hand side of the cut */
6821    int                   nvars               /**< the number of variables with non-zero coefficients in the cut */
6822    )
6823 {
6824    SCIP_BENDERSCUTCUT* cut;
6825 
6826    assert(benders != NULL);
6827    assert(set != NULL);
6828    assert(vars != NULL);
6829    assert(vals != NULL);
6830 
6831    /* allocating the block memory for the cut storage */
6832    SCIP_CALL( SCIPallocBlockMemory(set->scip, &cut) );
6833 
6834    /* storing the cut data */
6835    SCIP_CALL( SCIPduplicateBlockMemoryArray(set->scip, &cut->vars, vars, nvars) );
6836    SCIP_CALL( SCIPduplicateBlockMemoryArray(set->scip, &cut->vals, vals, nvars) );
6837    cut->lhs = lhs;
6838    cut->rhs = rhs;
6839    cut->nvars = nvars;
6840 
6841    /* ensuring the required memory is available for the stored cuts array */
6842    if( benders->storedcutssize < benders->nstoredcuts + 1 )
6843    {
6844       int newsize;
6845 
6846       newsize = SCIPsetCalcMemGrowSize(set, benders->nstoredcuts + 1);
6847       SCIP_ALLOC( BMSreallocBlockMemoryArray(SCIPblkmem(set->scip), &benders->storedcuts,
6848             benders->storedcutssize, newsize) );
6849 
6850       benders->storedcutssize = newsize;
6851    }
6852    assert(benders->storedcutssize >= benders->nstoredcuts + 1);
6853 
6854    /* adding the cuts to the Benders' cut storage */
6855    benders->storedcuts[benders->nstoredcuts] = cut;
6856    benders->nstoredcuts++;
6857 
6858    return SCIP_OKAY;
6859 }
6860 
6861 /** sets the sorted flags in the Benders' decomposition */
SCIPbendersSetBenderscutsSorted(SCIP_BENDERS * benders,SCIP_Bool sorted)6862 void SCIPbendersSetBenderscutsSorted(
6863    SCIP_BENDERS*         benders,            /**< Benders' decomposition structure */
6864    SCIP_Bool             sorted              /**< the value to set the sorted flag to */
6865    )
6866 {
6867    assert(benders != NULL);
6868 
6869    benders->benderscutssorted = sorted;
6870    benders->benderscutsnamessorted = sorted;
6871 }
6872 
6873 /** inserts a Benders' cut into the Benders' cuts list */
SCIPbendersIncludeBenderscut(SCIP_BENDERS * benders,SCIP_SET * set,SCIP_BENDERSCUT * benderscut)6874 SCIP_RETCODE SCIPbendersIncludeBenderscut(
6875    SCIP_BENDERS*         benders,            /**< Benders' decomposition structure */
6876    SCIP_SET*             set,                /**< global SCIP settings */
6877    SCIP_BENDERSCUT*      benderscut          /**< Benders' cut */
6878    )
6879 {
6880    assert(benders != NULL);
6881    assert(benderscut != NULL);
6882 
6883    if( benders->nbenderscuts >= benders->benderscutssize )
6884    {
6885       benders->benderscutssize = SCIPsetCalcMemGrowSize(set, benders->nbenderscuts+1);
6886       SCIP_ALLOC( BMSreallocMemoryArray(&benders->benderscuts, benders->benderscutssize) );
6887    }
6888    assert(benders->nbenderscuts < benders->benderscutssize);
6889 
6890    benders->benderscuts[benders->nbenderscuts] = benderscut;
6891    benders->nbenderscuts++;
6892    benders->benderscutssorted = FALSE;
6893 
6894    return SCIP_OKAY;
6895 }
6896 
6897 /** returns the Benders' cut of the given name, or NULL if not existing */
SCIPfindBenderscut(SCIP_BENDERS * benders,const char * name)6898 SCIP_BENDERSCUT* SCIPfindBenderscut(
6899    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
6900    const char*           name                /**< name of Benderscut' decomposition */
6901    )
6902 {
6903    int i;
6904 
6905    assert(benders != NULL);
6906    assert(name != NULL);
6907 
6908    for( i = 0; i < benders->nbenderscuts; i++ )
6909    {
6910       if( strcmp(SCIPbenderscutGetName(benders->benderscuts[i]), name) == 0 )
6911          return benders->benderscuts[i];
6912    }
6913 
6914    return NULL;
6915 }
6916 
6917 /** returns the array of currently available Benders' cuts; active Benders' decomposition are in the first slots of
6918  * the array
6919  */
SCIPbendersGetBenderscuts(SCIP_BENDERS * benders)6920 SCIP_BENDERSCUT** SCIPbendersGetBenderscuts(
6921    SCIP_BENDERS*         benders             /**< Benders' decomposition */
6922    )
6923 {
6924    assert(benders != NULL);
6925 
6926    if( !benders->benderscutssorted )
6927    {
6928       SCIPsortPtr((void**)benders->benderscuts, SCIPbenderscutComp, benders->nbenderscuts);
6929       benders->benderscutssorted = TRUE;
6930       benders->benderscutsnamessorted = FALSE;
6931    }
6932 
6933    return benders->benderscuts;
6934 }
6935 
6936 /** returns the number of currently available Benders' cuts */
SCIPbendersGetNBenderscuts(SCIP_BENDERS * benders)6937 int SCIPbendersGetNBenderscuts(
6938    SCIP_BENDERS*         benders             /**< Benders' decomposition */
6939    )
6940 {
6941    assert(benders != NULL);
6942 
6943    return benders->nbenderscuts;
6944 }
6945 
6946 /** sets the priority of a Benders' decomposition */
SCIPbendersSetBenderscutPriority(SCIP_BENDERS * benders,SCIP_BENDERSCUT * benderscut,int priority)6947 SCIP_RETCODE SCIPbendersSetBenderscutPriority(
6948    SCIP_BENDERS*         benders,            /**< Benders' decomposition */
6949    SCIP_BENDERSCUT*      benderscut,         /**< Benders' cut */
6950    int                   priority            /**< new priority of the Benders' decomposition */
6951    )
6952 {
6953    assert(benders != NULL);
6954    assert(benderscut != NULL);
6955 
6956    benderscut->priority = priority;
6957    benders->benderscutssorted = FALSE;
6958 
6959    return SCIP_OKAY;
6960 }
6961 
6962 /** sorts Benders' decomposition cuts by priorities */
SCIPbendersSortBenderscuts(SCIP_BENDERS * benders)6963 void SCIPbendersSortBenderscuts(
6964    SCIP_BENDERS*         benders             /**< Benders' decomposition */
6965    )
6966 {
6967    assert(benders != NULL);
6968 
6969    if( !benders->benderscutssorted )
6970    {
6971       SCIPsortPtr((void**)benders->benderscuts, SCIPbenderscutComp, benders->nbenderscuts);
6972       benders->benderscutssorted = TRUE;
6973       benders->benderscutsnamessorted = FALSE;
6974    }
6975 }
6976 
6977 /** sorts Benders' decomposition cuts by name */
SCIPbendersSortBenderscutsName(SCIP_BENDERS * benders)6978 void SCIPbendersSortBenderscutsName(
6979    SCIP_BENDERS*         benders             /**< Benders' decomposition */
6980    )
6981 {
6982    assert(benders != NULL);
6983 
6984    if( !benders->benderscutsnamessorted )
6985    {
6986       SCIPsortPtr((void**)benders->benderscuts, SCIPbenderscutCompName, benders->nbenderscuts);
6987       benders->benderscutssorted = FALSE;
6988       benders->benderscutsnamessorted = TRUE;
6989    }
6990 }
6991