Scippy

SCIP

Solving Constraint Integer Programs

cons_benders.c
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2/* */
3/* This file is part of the program and library */
4/* SCIP --- Solving Constraint Integer Programs */
5/* */
6/* Copyright (c) 2002-2025 Zuse Institute Berlin (ZIB) */
7/* */
8/* Licensed under the Apache License, Version 2.0 (the "License"); */
9/* you may not use this file except in compliance with the License. */
10/* You may obtain a copy of the License at */
11/* */
12/* http://www.apache.org/licenses/LICENSE-2.0 */
13/* */
14/* Unless required by applicable law or agreed to in writing, software */
15/* distributed under the License is distributed on an "AS IS" BASIS, */
16/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
17/* See the License for the specific language governing permissions and */
18/* limitations under the License. */
19/* */
20/* You should have received a copy of the Apache-2.0 license */
21/* along with SCIP; see the file LICENSE. If not visit scipopt.org. */
22/* */
23/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
24
25/**@file cons_benders.c
26 * @ingroup DEFPLUGINS_CONS
27 * @brief constraint handler for Benders' decomposition
28 * @author Stephen J. Maher
29 *
30 * Two constraint handlers are implemented for the generation of Benders' decomposition cuts. When included in a
31 * problem, the Benders' decomposition constraint handlers generate cuts during the enforcement of LP and relaxation
32 * solutions. Additionally, Benders' decomposition cuts can be generated when checking the feasibility of solutions with
33 * respect to the subproblem constraints.
34 *
35 * This constraint handler has an enforcement priority that is less than the integer constraint handler. This means that
36 * only integer feasible solutions from the LP solver are enforced by this constraint handler. This is the traditional
37 * behaviour of the branch-and-check approach to Benders' decomposition. Additionally, the check priority is set low,
38 * such that this expensive constraint handler is only called as a final check on primal feasible solutions.
39 *
40 * This constraint handler in the standard constraint handler that should be added when using Benders' decomposition.
41 * Additionally, there is a flag in SCIPincludeConshdlrBenders that permits the addition of the LP constraint handler,
42 * cons_benderslp. The use of both cons_benders and cons_benderslp allows the user to perform a multiphase Benders'
43 * decomposition algorithm.
44 */
45
46/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
47
48#include <assert.h>
49#include <string.h>
50
51#include "scip/scip.h"
52#include "scip/cons_benders.h"
53#include "scip/heur_trysol.h"
54#include "scip/heuristics.h"
55
56
57/* fundamental constraint handler properties */
58#define CONSHDLR_NAME "benders"
59#define CONSHDLR_DESC "constraint handler to execute Benders' Decomposition"
60#define CONSHDLR_ENFOPRIORITY -100 /**< priority of the constraint handler for constraint enforcing */
61#define CONSHDLR_CHECKPRIORITY -5000000 /**< priority of the constraint handler for checking feasibility */
62#define CONSHDLR_EAGERFREQ 100 /**< frequency for using all instead of only the useful constraints in separation,
63 * propagation and enforcement, -1 for no eager evaluations, 0 for first only */
64#define CONSHDLR_MAXPREROUNDS 0 /**< maximal number of presolving rounds the constraint handler participates in (-1: no limit) */
65#define CONSHDLR_PRESOLTIMING SCIP_PRESOLTIMING_FAST /**< presolving timing of the constraint handler (fast, medium, or exhaustive) */
66#define CONSHDLR_NEEDSCONS FALSE /**< should the constraint handler be skipped, if no constraints are available? */
67
68
69#define DEFAULT_CHECKEDSOLSSIZE 20 /**< initial size of the checked sols array */
70#define DEFAULT_ACTIVE FALSE /**< is the constraint handler active? */
71
72/*
73 * Data structures
74 */
75
76/** constraint handler data */
77struct SCIP_ConshdlrData
78{
79 int* checkedsols; /**< an array of solutions that this constraint has already checked */
80 int ncheckedsols; /**< the number of checked solutions */
81 int checkedsolssize; /**< the size of the checked solutions array */
82 SCIP_Bool active; /**< is the constraint handler active? */
83};
84
85/*
86 * Local methods
87 */
88
89/** constructs a new solution based upon the solutions to the Benders' decomposition subproblems */
90static
92 SCIP* scip, /**< the SCIP instance */
93 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
94 SCIP_SOL* sol, /**< primal CIP solution */
95 SCIP_BENDERSENFOTYPE type /**< the type of solution being enforced */
96 )
97{
98 SCIP_CONSHDLRDATA* conshdlrdata;
99 SCIP_SOL* newsol;
100 SCIP_HEUR* heurtrysol;
101 SCIP_BENDERS** benders;
102 SCIP_VAR** auxiliaryvars;
103 int nactivebenders;
104 int nsubproblems;
105 int i;
106 int j;
107 SCIP_Bool success = TRUE;
108
109 /* don't propose new solutions if not in presolve or solving */
111 return SCIP_OKAY;
112
113 conshdlrdata = SCIPconshdlrGetData(conshdlr);
114 assert(conshdlrdata != NULL);
115
116 benders = SCIPgetBenders(scip);
117 nactivebenders = SCIPgetNActiveBenders(scip);
118
119 /* if the solution is NULL, then we create the solution from the LP sol */
120 if( sol != NULL )
121 {
122 assert(type == SCIP_BENDERSENFOTYPE_CHECK);
123 SCIP_CALL( SCIPcreateSolCopy(scip, &newsol, sol) );
124 }
125 else
126 {
127 switch( type )
128 {
130 SCIP_CALL( SCIPcreateLPSol(scip, &newsol, NULL) );
131 break;
134 break;
137 break;
138 default:
139 SCIP_CALL( SCIPcreateLPSol(scip, &newsol, NULL) );
140 break;
141 } /*lint !e788*/
142 }
143 SCIP_CALL( SCIPunlinkSol(scip, newsol) );
144
145 /* looping through all Benders' decompositions to construct the new solution */
146 for( i = 0; i < nactivebenders; i++ )
147 {
148 SCIP_VAR* masterauxvar;
149 SCIP_BENDERSOBJTYPE objtype;
150 SCIP_Real masterauxvarval = 0.0;
151
152 /* getting the objective type for the subproblems */
153 objtype = SCIPbendersGetObjectiveType(benders[i]);
154
155 /* getting the auxiliary variables and the number of subproblems from the Benders' decomposition structure */
156 masterauxvar = SCIPbenderGetMasterAuxiliaryVar(benders[i]);
157 auxiliaryvars = SCIPbendersGetAuxiliaryVars(benders[i]);
158 nsubproblems = SCIPbendersGetNSubproblems(benders[i]);
159
160 /* setting the auxiliary variable in the new solution */
161 for( j = 0; j < nsubproblems; j++ )
162 {
163 SCIP_Real objval;
164
165 objval = SCIPbendersGetSubproblemObjval(benders[i], j);
166
167 if( SCIPvarGetStatus(auxiliaryvars[j]) == SCIP_VARSTATUS_FIXED
168 && !SCIPisEQ(scip, SCIPgetSolVal(scip, newsol, auxiliaryvars[j]), objval) )
169 {
170 success = FALSE;
171 break;
172 }
173 else if( SCIPisLT(scip, SCIPgetSolVal(scip, newsol, auxiliaryvars[j]), objval) )
174 {
175 SCIP_CALL( SCIPsetSolVal(scip, newsol, auxiliaryvars[j], objval) );
176 }
177
178 if( objtype == SCIP_BENDERSOBJTYPE_SUM )
179 {
180 masterauxvarval += objval;
181 }
182 else
183 {
184 assert(objtype == SCIP_BENDERSOBJTYPE_MAX);
185 masterauxvarval = MAX(masterauxvarval, objval);
186 }
187 }
188
189 /* setting the value of the master auxiliary variable. It is possible that the master auxiliary variable is
190 * removed during presolve. As such, it could be NULL in sub-SCIPs or inactive in the original SCIP.
191 */
192 if( masterauxvar != NULL && SCIPvarIsActive(masterauxvar) )
193 {
194 SCIP_CALL( SCIPsetSolVal(scip, newsol, SCIPbenderGetMasterAuxiliaryVar(benders[i]), masterauxvarval) );
195 }
196
197 if( !success )
198 break;
199 }
200
201 /* if setting the variable values was successful, then we try to add the solution */
202 if( success ) /*lint !e774*/
203 {
204 /* checking the size of the checkedsols array and extending it is there is not enough memory */
205 assert(conshdlrdata->ncheckedsols <= conshdlrdata->checkedsolssize);
206 if( conshdlrdata->ncheckedsols + 1 > conshdlrdata->checkedsolssize )
207 {
208 int newsize;
209
210 newsize = SCIPcalcMemGrowSize(scip, conshdlrdata->ncheckedsols + 1);
211 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &conshdlrdata->checkedsols, conshdlrdata->checkedsolssize, newsize) );
212 conshdlrdata->checkedsolssize = newsize;
213 }
214 assert(conshdlrdata->ncheckedsols + 1 <= conshdlrdata->checkedsolssize);
215
216 /* recording the solution number to avoid checking the solution again */
217 conshdlrdata->checkedsols[conshdlrdata->ncheckedsols] = SCIPsolGetIndex(newsol);
218 conshdlrdata->ncheckedsols++;
219
220 /* getting the try solution heuristic */
221 heurtrysol = SCIPfindHeur(scip, "trysol");
222
223 /* passing the new solution to the trysol heuristic */
224 SCIP_CALL( SCIPcheckSol(scip, newsol, FALSE, FALSE, TRUE, TRUE, TRUE, &success) );
225 if ( success )
226 {
227 SCIP_CALL( SCIPheurPassSolAddSol(scip, heurtrysol, newsol) );
228 SCIPdebugMsg(scip, "Creating solution was successful.\n");
229 }
230 else
231 {
232 /* the solution might not be feasible, because of additional constraints */
233 SCIPdebugMsg(scip, "Creating solution was not successful.\n");
234 }
235 }
236
237 SCIP_CALL( SCIPfreeSol(scip, &newsol) );
238
239 return SCIP_OKAY;
240}
241
242/** checks the Benders' decomposition auxiliary variables for unboundedness. */
243static
245 SCIP* scip, /**< the SCIP data structure */
246 SCIP_BENDERS* benders, /**< the Benders' decomposition data structure */
247 SCIP_SOL* sol /**< the primal solution to enforce, or NULL for the current LP/pseudo sol */
248 )
249{
250 int nsubproblems;
251 SCIP_Bool unbounded = FALSE;
252 int i;
253
254 assert(scip != NULL);
255 assert(benders != NULL);
256
257 nsubproblems = SCIPbendersGetNSubproblems(benders);
258
259 /* checking the auxiliary variable values for unboundedness */
260 for( i = 0; i < nsubproblems; i++ )
261 {
263 || SCIPisInfinity(scip, -SCIPgetBendersAuxiliaryVarVal(scip, benders, sol, i)) )
264 {
265 unbounded = TRUE;
266 break;
267 }
268 }
269
270 return unbounded;
271}
272
273/** enforces Benders' constraints for given solution
274 *
275 * This method is called from cons_benderslp and cons_benders. If the method is called from cons_benderslp, then the
276 * solutions are not guaranteed to be integer feasible. This is because the default priority is set greater than the
277 * integer constraint handler. If this method is called from cons_benders, then, because the default enforcement
278 * priority is set less than that of the integer constraint handler, then it can be assumed that the solutions are
279 * integer feasible.
280 *
281 * The checkint flag indicates whether integer feasibility can be assumed. If it is not assumed, i.e. checkint ==
282 * FALSE, then only the convex relaxations of the subproblems are solved. If integer feasibility is assumed, i.e.
283 * checkint == TRUE, then the convex relaxations and the full CIP are solved to generate Benders' cuts and check
284 * solution feasibility.
285 */
287 SCIP* scip, /**< the SCIP instance */
288 SCIP_SOL* sol, /**< the primal solution to enforce, or NULL for the current LP/pseudo sol */
289 SCIP_CONSHDLR* conshdlr, /**< the constraint handler */
290 SCIP_RESULT* result, /**< the result of the enforcement */
291 SCIP_BENDERSENFOTYPE type, /**< the type of solution being enforced */
292 SCIP_Bool checkint /**< should integrality be considered when checking the subproblems */
293 )
294{
295 SCIP_BENDERS** benders;
296 SCIP_Bool infeasible;
297 SCIP_Bool auxviol;
298 int nactivebenders;
299 int i;
300
301 assert(scip != NULL);
302 assert(conshdlr != NULL);
303 assert(result != NULL);
304
305 (*result) = SCIP_FEASIBLE;
306 infeasible = FALSE;
307 auxviol = FALSE;
308
309 benders = SCIPgetBenders(scip);
310 nactivebenders = SCIPgetNActiveBenders(scip);
311
312 for( i = 0; i < nactivebenders; i++ )
313 {
314 /* if any subproblems are declared as infeasible, then the result must be returned as infeasible. It is not
315 * possible to generate cuts, since the LP is infeasible without performing any master variable fixing
316 */
318 {
319 (*result) = SCIP_INFEASIBLE;
320
321 /* the Benders' decomposition subproblems do not need to be checked, since there is a subproblem that is
322 * infeasible.
323 */
324 break;
325 }
326
327 switch( type )
328 {
330 if( SCIPbendersCutLP(benders[i]) )
331 {
332 SCIP_Bool unbounded = FALSE;
333
334 /* if the solution is unbounded, then it may not be possible to generate any Benders' decomposition
335 * cuts. If the unboundedness is from the auxiliary variables, then cuts are required. Otherwise, if
336 * the unboundedness comes from original variables, then the unboundedness needs to be handled by other
337 * constraint handlers or the problem is reported as unbounded
338 * */
340 {
341 if( !unboundedAuxiliaryVariables(scip, benders[i], NULL) )
342 {
343 (*result) = SCIP_FEASIBLE;
344 auxviol = FALSE;
345 unbounded = TRUE;
346 }
347 }
348
349 if( !unbounded )
350 {
351 SCIP_CALL( SCIPsolveBendersSubproblems(scip, benders[i], NULL, result, &infeasible, &auxviol, type, checkint) );
352 }
353 }
354 break;
356 if( SCIPbendersCutRelaxation(benders[i]) )
357 {
358 SCIP_CALL( SCIPsolveBendersSubproblems(scip, benders[i], sol, result, &infeasible, &auxviol, type, checkint) );
359 }
360 break;
362 if( SCIPbendersCutPseudo(benders[i]) )
363 {
364 SCIP_CALL( SCIPsolveBendersSubproblems(scip, benders[i], NULL, result, &infeasible, &auxviol, type, checkint) );
365 }
366 break;
368 SCIPwarningMessage(scip, "The conscheck callback is not supported\n");
369 break;
370 default:
371 break;
372 } /*lint !e788*/
373
374 /* The decompositions are checked until one is found that is not feasible. Not being feasible could mean that
375 * infeasibility of the original problem has been proven or a constraint has been added. If the result
376 * SCIP_DIDNOTRUN is returned, then the next decomposition is checked */
377 if( (*result) != SCIP_FEASIBLE && (*result) != SCIP_DIDNOTRUN )
378 break;
379 }
380
381 /* if the constraint handler was called with an integer feasible solution, then a feasible solution can be proposed */
382 if( checkint )
383 {
384 /* in the case that the problem is feasible, this means that all subproblems are feasible. The auxiliary variables
385 * still need to be updated. This is done by constructing a valid solution. */
386 if( (*result) == SCIP_FEASIBLE && auxviol )
387 {
388 SCIP_CALL( constructValidSolution(scip, conshdlr, sol, type) );
389
390 (*result) = SCIP_INFEASIBLE;
391 }
392 }
393
394 /* if no Benders' decomposition were run, then the result is returned as SCIP_FEASIBLE. The SCIP_DIDNOTRUN result
395 * indicates that no subproblems were checked or that cuts were disabled, so that it is not guaranteed that this
396 * solution is feasible.
397 */
398 if( (*result) == SCIP_DIDNOTRUN )
399 (*result) = SCIP_FEASIBLE;
400
401 return SCIP_OKAY;
402}
403
404/*
405 * Callback methods of constraint handler
406 */
407
408/** copy method for constraint handler plugins (called when SCIP copies plugins) */
409static
410SCIP_DECL_CONSHDLRCOPY(conshdlrCopyBenders)
411{ /*lint --e{715}*/
412 assert(scip != NULL);
413
415
416 *valid = TRUE;
417
418 return SCIP_OKAY;
419}
420
421/** destructor of constraint handler to free constraint handler data (called when SCIP is exiting) */
422static
423SCIP_DECL_CONSFREE(consFreeBenders)
424{ /*lint --e{715}*/
425 SCIP_CONSHDLRDATA* conshdlrdata;
426
427 assert(scip != NULL);
428 assert(conshdlr != NULL);
429
430 conshdlrdata = SCIPconshdlrGetData(conshdlr);
431 assert(conshdlrdata != NULL);
432
433 /* freeing the constraint handler data */
434 SCIPfreeMemory(scip, &conshdlrdata);
435
436 return SCIP_OKAY;
437}
438
439
440/** initialization method of constraint handler (called after problem was transformed) */
441static
442SCIP_DECL_CONSINIT(consInitBenders)
443{ /*lint --e{715}*/
444 SCIP_CONSHDLRDATA* conshdlrdata;
445
446 assert(scip != NULL);
447 assert(conshdlr != NULL);
448
449 conshdlrdata = SCIPconshdlrGetData(conshdlr);
450 assert(conshdlrdata != NULL);
451
452 /* disable benders handler */
453 if( !conshdlrdata->active )
454 {
456 return SCIP_OKAY;
457 }
458
459 conshdlrdata->checkedsolssize = DEFAULT_CHECKEDSOLSSIZE;
460 conshdlrdata->ncheckedsols = 0;
461
462 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &conshdlrdata->checkedsols, conshdlrdata->checkedsolssize) );
463
464 return SCIP_OKAY;
465}
466
467
468/** deinitialization method of constraint handler (called before transformed problem is freed) */
469static
470SCIP_DECL_CONSEXIT(consExitBenders)
471{ /*lint --e{715}*/
472 SCIP_CONSHDLRDATA* conshdlrdata;
473
474 assert(scip != NULL);
475 assert(conshdlr != NULL);
476
477 conshdlrdata = SCIPconshdlrGetData(conshdlr);
478 assert(conshdlrdata != NULL);
479
480 /* reenable benders handler */
481 if( SCIPconshdlrNeedsCons(conshdlr) )
482 {
483 assert(!conshdlrdata->active);
485 return SCIP_OKAY;
486 }
487
488 /* freeing the checked sols array */
489 SCIPfreeBlockMemoryArray(scip, &conshdlrdata->checkedsols, conshdlrdata->checkedsolssize);
490
491 return SCIP_OKAY;
492}
493
494
495/** LP initialization method of constraint handler (called before the initial LP relaxation at a node is solved) */
496static
497SCIP_DECL_CONSINITLP(consInitlpBenders)
498{ /*lint --e{715}*/
499 SCIP_BENDERS** benders;
500 int nactivebenders;
501 int i;
502
503 assert(scip != NULL);
504 assert(conshdlr != NULL);
505
506 benders = SCIPgetBenders(scip);
507 nactivebenders = SCIPgetNActiveBenders(scip);
508
509 (*infeasible) = FALSE;
510
511 /* checking all Benders' decomposition implementations to see if any subproblems have been declared as infeasible */
512 for( i = 0; i < nactivebenders && !(*infeasible); i++ )
513 (*infeasible) = SCIPbendersSubproblemsAreInfeasible(benders[i]);
514
515 return SCIP_OKAY;
516}
517
518/** constraint enforcing method of constraint handler for LP solutions */
519static
520SCIP_DECL_CONSENFOLP(consEnfolpBenders)
521{ /*lint --e{715}*/
522 assert(scip != NULL);
523 assert(conshdlr != NULL);
524
526
527 return SCIP_OKAY;
528}
529
530
531/** constraint enforcing method of constraint handler for relaxation solutions */
532static
533SCIP_DECL_CONSENFORELAX(consEnforelaxBenders)
534{ /*lint --e{715}*/
535 assert(scip != NULL);
536 assert(conshdlr != NULL);
537
539
540 return SCIP_OKAY;
541}
542
543
544/** constraint enforcing method of constraint handler for pseudo solutions */
545static
546SCIP_DECL_CONSENFOPS(consEnfopsBenders)
547{ /*lint --e{715}*/
548 assert(scip != NULL);
549 assert(conshdlr != NULL);
550
552
553 return SCIP_OKAY;
554}
555
556
557/** feasibility check method of constraint handler for integral solutions
558 *
559 * This function checks the feasibility of the Benders' decomposition master problem. In the case that the problem is
560 * feasible, then the auxiliary variables must be updated with the subproblem objective function values. It is not
561 * possible to simply update the auxiliary variable values, so a new solution is created.
562 */
563static
564SCIP_DECL_CONSCHECK(consCheckBenders)
565{ /*lint --e{715}*/
566 SCIP_CONSHDLRDATA* conshdlrdata;
567 SCIP_BENDERS** benders;
568 int nactivebenders;
569 int solindex;
570 int i;
571 SCIP_Bool performcheck;
572 SCIP_Bool auxviol;
573
574 assert(scip != NULL);
575 assert(conshdlr != NULL);
576 assert(result != NULL);
577
578 *result = SCIP_FEASIBLE;
579
580 conshdlrdata = SCIPconshdlrGetData(conshdlr);
581 assert(conshdlrdata != NULL);
582
583 if( !conshdlrdata->active )
584 return SCIP_OKAY;
585
586 performcheck = TRUE;
587 auxviol = FALSE;
588
589 /* if the constraint handler is active, then the check must be performed. */
590 benders = SCIPgetBenders(scip);
591 nactivebenders = SCIPgetNActiveBenders(scip);
592
593 /* checking if the solution was constructed by this constraint handler */
594 solindex = SCIPsolGetIndex(sol);
595 for( i = 0; i < conshdlrdata->ncheckedsols; i++ )
596 {
597 if( conshdlrdata->checkedsols[i] == solindex )
598 {
599 conshdlrdata->checkedsols[0] = conshdlrdata->checkedsols[conshdlrdata->ncheckedsols - 1];
600 conshdlrdata->ncheckedsols--;
601
602 performcheck = FALSE;
603 break;
604 }
605 }
606
607 /* if the solution has not been checked before, then we must perform the check */
608 if( performcheck && nactivebenders > 0 )
609 {
610 for( i = 0; i < nactivebenders; i++ )
611 {
612 /* if any subproblems are declared as infeasible, then the result must be returned as infeasible. It is not
613 * possible to generate cuts, since the LP is infeasible without performing any master variable fixing
614 */
616 {
617 (*result) = SCIP_INFEASIBLE;
618 }
619 else
620 {
621 SCIP_Bool infeasible;
622 infeasible = FALSE;
623
624 SCIP_CALL( SCIPsolveBendersSubproblems(scip, benders[i], sol, result, &infeasible, &auxviol,
626 }
627
628 /* in the case of multiple Benders' decompositions, the subproblems are solved until a constriant is added or
629 * infeasibility is proven. So if the result is not SCIP_FEASIBLE, then the loop is exited */
630 if( (*result) != SCIP_FEASIBLE )
631 break;
632 }
633
634 /* in the case that the problem is feasible, this means that all subproblems are feasible. The auxiliary variables
635 * still need to be updated. This is done by constructing a valid solution. */
636 if( (*result) == SCIP_FEASIBLE )
637 {
638 if( auxviol )
639 {
640 if( !SCIPsolIsOriginal(sol) )
641 {
643 }
644
645 if( printreason )
646 SCIPmessagePrintInfo(SCIPgetMessagehdlr(scip), "all subproblems are feasible but there is a violation in the auxiliary variables\n");
647
648 (*result) = SCIP_INFEASIBLE;
649 }
650 }
651
652 /* if no Benders' decomposition were run, then the result is returned as SCIP_FEASIBLE. The SCIP_DIDNOTRUN result
653 * indicates that no subproblems were checked or that cuts were disabled, so that it is not guaranteed that this
654 * solution is feasible.
655 */
656 if( (*result) == SCIP_DIDNOTRUN )
657 (*result) = SCIP_FEASIBLE;
658 }
659
660 return SCIP_OKAY;
661}
662
663
664/** the presolving method for the Benders' decomposition constraint handler
665 *
666 * This method is used to update the lower bounds of the auxiliary problem and to identify infeasibility before the
667 * subproblems are solved. When SCIP is copied, the Benders' decomposition subproblems from the source SCIP are
668 * transferred to the target SCIP. So there is no need to perform this presolving step in the copied SCIP, since the
669 * computed bounds would be identical.
670 */
671static
672SCIP_DECL_CONSPRESOL(consPresolBenders)
673{ /*lint --e{715}*/
674 SCIP_BENDERS** benders;
675 int nactivebenders;
676 int nsubproblems;
677 int i;
678 int j;
679
680 assert(scip != NULL);
681 assert(conshdlr != NULL);
682
683 (*result) = SCIP_DIDNOTFIND;
684
685 /* this presolving step is only valid for the main SCIP instance. If the SCIP instance is copied, then the presolving
686 * step is not performed.
687 */
688 if( SCIPgetSubscipDepth(scip) > 0 )
689 {
690 (*result) = SCIP_DIDNOTRUN;
691 return SCIP_OKAY;
692 }
693
694 /* it is only possible to compute the lower bound of the subproblems if the constraint handler is active */
695 benders = SCIPgetBenders(scip);
696 nactivebenders = SCIPgetNActiveBenders(scip);
697
698 /* need to compute the lower bound for all active Benders' decompositions */
699 for( i = 0; i < nactivebenders; i++ )
700 {
701 nsubproblems = SCIPbendersGetNSubproblems(benders[i]);
702
703 for( j = 0; j < nsubproblems; j++ )
704 {
705 SCIP_VAR* auxiliaryvar;
706 SCIP_Real lowerbound;
707 SCIP_Bool infeasible;
708
709 infeasible = FALSE;
710
711 /* computing the lower bound of the subproblem by solving it without any variable fixings */
712 SCIP_CALL( SCIPcomputeBendersSubproblemLowerbound(scip, benders[i], j, &lowerbound, &infeasible) );
713
714 if( infeasible )
715 {
716 (*result) = SCIP_CUTOFF;
717 break;
718 }
719
720 /* retrieving the auxiliary variable */
721 auxiliaryvar = SCIPbendersGetAuxiliaryVar(benders[i], j);
722
723 /* only update the lower bound if it is greater than the current lower bound */
724 if( SCIPisGT(scip, lowerbound, SCIPvarGetLbLocal(auxiliaryvar)) )
725 {
726 SCIPdebugMsg(scip, "Tightened lower bound of <%s> to %g\n", SCIPvarGetName(auxiliaryvar), lowerbound);
727 /* updating the lower bound of the auxiliary variable */
728 SCIP_CALL( SCIPchgVarLb(scip, auxiliaryvar, lowerbound) );
729
730 (*nchgbds)++;
731 (*result) = SCIP_SUCCESS;
732 }
733
734 /* stores the lower bound for the subproblem */
735 SCIPbendersUpdateSubproblemLowerbound(benders[i], j, lowerbound);
736 }
737
738 if( (*result) == SCIP_CUTOFF )
739 break;
740 }
741
742 return SCIP_OKAY;
743}
744
745/** variable rounding lock method of constraint handler
746 * The auxiliary variables and the master problem variables need to lock added by the Benders' decomposition
747 * constraint. The auxiliary variables require a down lock. The master problem variable need both up and down lock.
748 * The master problem variables require locks in both directions because the coefficients in all potential Benders'
749 * cuts are not known in general.
750 */
751static
752SCIP_DECL_CONSLOCK(consLockBenders)
753{ /*lint --e{715}*/
754 SCIP_CONSHDLRDATA* conshdlrdata;
755 SCIP_BENDERS** benders;
756 SCIP_VAR** vars;
757 int nactivebenders;
758 int nsubproblems;
759 int nvars;
760 int i;
761 int j;
762
763 assert(scip != NULL);
764 assert(conshdlr != NULL);
765 assert(locktype == SCIP_LOCKTYPE_MODEL);
766
767 conshdlrdata = SCIPconshdlrGetData(conshdlr);
768 assert(conshdlrdata != NULL);
769
770 if( !conshdlrdata->active )
771 return SCIP_OKAY;
772
773 /* the locks should only be added if the Benders' decomposition constraint handler has been activated */
774 benders = SCIPgetBenders(scip);
775 nactivebenders = SCIPgetNActiveBenders(scip);
776
777 /* retrieving the master problem variables */
778 SCIP_CALL( SCIPgetOrigVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL) );
779
780 /* need to compute the lower bound for all active Benders' decompositions */
781 for( i = 0; i < nactivebenders; i++ )
782 {
783 nsubproblems = SCIPbendersGetNSubproblems(benders[i]);
784
785 /* if the auxiliary variable exists, then we need to add a down lock. Initially, a down lock is added to all
786 * auxiliary variables during creating. This is because the creation of auxiliary variable occurs after
787 * CONS_LOCK is called. The inclusion of the auxiliary variables in this function is to cover the case if locks
788 * are added or removed after presolving.
789 */
790 for( j = 0; j < nsubproblems; j++ )
791 {
792 SCIP_VAR* auxvar;
793
794 auxvar = SCIPbendersGetAuxiliaryVar(benders[i], j);
795
796 if( auxvar != NULL )
797 {
798 SCIP_CALL( SCIPaddVarLocksType(scip, auxvar, locktype, nlockspos, nlocksneg) );
799 }
800 }
801
802 /* adding up and down locks for all master problem variables. Since the locks for all constraint handlers
803 * without constraints, no auxiliary variables have been added. As such, all variables are master variables.
804 */
805 for( j = 0; j < nvars; j++ )
806 {
807 SCIP_CALL( SCIPaddVarLocksType(scip, vars[j], locktype, (nlockspos + nlocksneg)*nsubproblems,
808 (nlockspos + nlocksneg)*nsubproblems) );
809 }
810 }
811
812 return SCIP_OKAY;
813}
814
815
816/*
817 * constraint specific interface methods
818 */
819
820/** creates the handler for Benders' decomposition and includes it in SCIP */
822 SCIP* scip /**< SCIP data structure */
823 )
824{
825 SCIP_CONSHDLRDATA* conshdlrdata;
826 SCIP_CONSHDLR* conshdlr;
827
828 /* create benders constraint handler data */
829 conshdlrdata = NULL;
830
831 SCIP_CALL( SCIPallocMemory(scip, &conshdlrdata) );
832
833 conshdlr = NULL;
834
835 /* include constraint handler */
838 consEnfolpBenders, consEnfopsBenders, consCheckBenders, consLockBenders,
839 conshdlrdata) );
840 assert(conshdlr != NULL);
841
842 /* set non-fundamental callbacks via specific setter functions */
843 SCIP_CALL( SCIPsetConshdlrInit(scip, conshdlr, consInitBenders) );
844 SCIP_CALL( SCIPsetConshdlrExit(scip, conshdlr, consExitBenders) );
845 SCIP_CALL( SCIPsetConshdlrCopy(scip, conshdlr, conshdlrCopyBenders, NULL) );
846 SCIP_CALL( SCIPsetConshdlrFree(scip, conshdlr, consFreeBenders) );
847 SCIP_CALL( SCIPsetConshdlrInitlp(scip, conshdlr, consInitlpBenders) );
848 SCIP_CALL( SCIPsetConshdlrEnforelax(scip, conshdlr, consEnforelaxBenders) );
850
851 /* add Benders' decomposition constraint handler parameters */
853 "constraints/" CONSHDLR_NAME "/active", "is the Benders' decomposition constraint handler active?",
854 &conshdlrdata->active, FALSE, DEFAULT_ACTIVE, NULL, NULL));
855
856 return SCIP_OKAY;
857}
static GRAPHNODE ** active
static SCIP_RETCODE constructValidSolution(SCIP *scip, SCIP_CONSHDLR *conshdlr, SCIP_SOL *sol, SCIP_BENDERSENFOTYPE type)
Definition: cons_benders.c:91
#define CONSHDLR_NEEDSCONS
Definition: cons_benders.c:66
static SCIP_DECL_CONSINIT(consInitBenders)
Definition: cons_benders.c:442
#define CONSHDLR_CHECKPRIORITY
Definition: cons_benders.c:61
#define CONSHDLR_DESC
Definition: cons_benders.c:59
static SCIP_DECL_CONSEXIT(consExitBenders)
Definition: cons_benders.c:470
static SCIP_DECL_CONSLOCK(consLockBenders)
Definition: cons_benders.c:752
#define CONSHDLR_MAXPREROUNDS
Definition: cons_benders.c:64
static SCIP_DECL_CONSCHECK(consCheckBenders)
Definition: cons_benders.c:564
static SCIP_DECL_CONSFREE(consFreeBenders)
Definition: cons_benders.c:423
static SCIP_DECL_CONSENFOLP(consEnfolpBenders)
Definition: cons_benders.c:520
static SCIP_DECL_CONSPRESOL(consPresolBenders)
Definition: cons_benders.c:672
static SCIP_DECL_CONSHDLRCOPY(conshdlrCopyBenders)
Definition: cons_benders.c:410
#define DEFAULT_CHECKEDSOLSSIZE
Definition: cons_benders.c:69
static SCIP_DECL_CONSENFORELAX(consEnforelaxBenders)
Definition: cons_benders.c:533
#define CONSHDLR_PRESOLTIMING
Definition: cons_benders.c:65
static SCIP_DECL_CONSENFOPS(consEnfopsBenders)
Definition: cons_benders.c:546
#define CONSHDLR_EAGERFREQ
Definition: cons_benders.c:62
#define CONSHDLR_ENFOPRIORITY
Definition: cons_benders.c:60
static SCIP_Bool unboundedAuxiliaryVariables(SCIP *scip, SCIP_BENDERS *benders, SCIP_SOL *sol)
Definition: cons_benders.c:244
#define CONSHDLR_NAME
Definition: cons_benders.c:58
static SCIP_DECL_CONSINITLP(consInitlpBenders)
Definition: cons_benders.c:497
#define DEFAULT_ACTIVE
Definition: cons_benders.c:70
constraint handler for Benders' decomposition
#define NULL
Definition: def.h:248
#define SCIP_Bool
Definition: def.h:91
#define SCIP_Real
Definition: def.h:156
#define TRUE
Definition: def.h:93
#define FALSE
Definition: def.h:94
#define MAX(x, y)
Definition: def.h:220
#define SCIP_CALL(x)
Definition: def.h:355
SCIP_RETCODE SCIPconsBendersEnforceSolution(SCIP *scip, SCIP_SOL *sol, SCIP_CONSHDLR *conshdlr, SCIP_RESULT *result, SCIP_BENDERSENFOTYPE type, SCIP_Bool checkint)
Definition: cons_benders.c:286
SCIP_RETCODE SCIPincludeConshdlrBenders(SCIP *scip)
Definition: cons_benders.c:821
int SCIPgetSubscipDepth(SCIP *scip)
Definition: scip_copy.c:2588
SCIP_STAGE SCIPgetStage(SCIP *scip)
Definition: scip_general.c:444
SCIP_RETCODE SCIPgetOrigVarsData(SCIP *scip, SCIP_VAR ***vars, int *nvars, int *nbinvars, int *nintvars, int *nimplvars, int *ncontvars)
Definition: scip_prob.c:2753
SCIP_MESSAGEHDLR * SCIPgetMessagehdlr(SCIP *scip)
Definition: scip_message.c:88
#define SCIPdebugMsg
Definition: scip_message.h:78
void SCIPwarningMessage(SCIP *scip, const char *formatstr,...)
Definition: scip_message.c:120
SCIP_RETCODE SCIPheurPassSolAddSol(SCIP *scip, SCIP_HEUR *heur, SCIP_SOL *sol)
Definition: heur_trysol.c:296
SCIP_RETCODE SCIPaddBoolParam(SCIP *scip, const char *name, const char *desc, SCIP_Bool *valueptr, SCIP_Bool isadvanced, SCIP_Bool defaultvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
Definition: scip_param.c:57
int SCIPgetNActiveBenders(SCIP *scip)
Definition: scip_benders.c:532
SCIP_BENDERSOBJTYPE SCIPbendersGetObjectiveType(SCIP_BENDERS *benders)
Definition: benders.c:6852
SCIP_Bool SCIPbendersCutRelaxation(SCIP_BENDERS *benders)
Definition: benders.c:6145
SCIP_BENDERS ** SCIPgetBenders(SCIP *scip)
Definition: scip_benders.c:508
SCIP_RETCODE SCIPcomputeBendersSubproblemLowerbound(SCIP *scip, SCIP_BENDERS *benders, int probnumber, SCIP_Real *lowerbound, SCIP_Bool *infeasible)
Definition: scip_benders.c:984
SCIP_VAR * SCIPbendersGetAuxiliaryVar(SCIP_BENDERS *benders, int probnumber)
Definition: benders.c:6213
SCIP_Real SCIPgetBendersAuxiliaryVarVal(SCIP *scip, SCIP_BENDERS *benders, SCIP_SOL *sol, int probnumber)
Definition: scip_benders.c:956
SCIP_Bool SCIPbendersSubproblemsAreInfeasible(SCIP_BENDERS *benders)
Definition: benders.c:6577
SCIP_Bool SCIPbendersCutPseudo(SCIP_BENDERS *benders)
Definition: benders.c:6135
SCIP_VAR ** SCIPbendersGetAuxiliaryVars(SCIP_BENDERS *benders)
Definition: benders.c:6225
int SCIPbendersGetNSubproblems(SCIP_BENDERS *benders)
Definition: benders.c:6011
void SCIPbendersUpdateSubproblemLowerbound(SCIP_BENDERS *benders, int probnumber, SCIP_Real lowerbound)
Definition: benders.c:6874
SCIP_Bool SCIPbendersCutLP(SCIP_BENDERS *benders)
Definition: benders.c:6125
SCIP_RETCODE SCIPsolveBendersSubproblems(SCIP *scip, SCIP_BENDERS *benders, SCIP_SOL *sol, SCIP_RESULT *result, SCIP_Bool *infeasible, SCIP_Bool *auxviol, SCIP_BENDERSENFOTYPE type, SCIP_Bool checkint)
Definition: scip_benders.c:647
SCIP_VAR * SCIPbenderGetMasterAuxiliaryVar(SCIP_BENDERS *benders)
Definition: benders.c:6203
SCIP_Real SCIPbendersGetSubproblemObjval(SCIP_BENDERS *benders, int probnumber)
Definition: benders.c:6302
SCIP_RETCODE SCIPsetConshdlrPresol(SCIP *scip, SCIP_CONSHDLR *conshdlr, SCIP_DECL_CONSPRESOL((*conspresol)), int maxprerounds, SCIP_PRESOLTIMING presoltiming)
Definition: scip_cons.c:540
SCIP_RETCODE SCIPsetConshdlrInit(SCIP *scip, SCIP_CONSHDLR *conshdlr, SCIP_DECL_CONSINIT((*consinit)))
Definition: scip_cons.c:396
SCIP_RETCODE SCIPincludeConshdlrBasic(SCIP *scip, SCIP_CONSHDLR **conshdlrptr, const char *name, const char *desc, int enfopriority, int chckpriority, int eagerfreq, SCIP_Bool needscons, SCIP_DECL_CONSENFOLP((*consenfolp)), SCIP_DECL_CONSENFOPS((*consenfops)), SCIP_DECL_CONSCHECK((*conscheck)), SCIP_DECL_CONSLOCK((*conslock)), SCIP_CONSHDLRDATA *conshdlrdata)
Definition: scip_cons.c:181
SCIP_RETCODE SCIPsetConshdlrFree(SCIP *scip, SCIP_CONSHDLR *conshdlr, SCIP_DECL_CONSFREE((*consfree)))
Definition: scip_cons.c:372
SCIP_RETCODE SCIPsetConshdlrEnforelax(SCIP *scip, SCIP_CONSHDLR *conshdlr, SCIP_DECL_CONSENFORELAX((*consenforelax)))
Definition: scip_cons.c:323
SCIP_RETCODE SCIPsetConshdlrExit(SCIP *scip, SCIP_CONSHDLR *conshdlr, SCIP_DECL_CONSEXIT((*consexit)))
Definition: scip_cons.c:420
SCIP_Bool SCIPconshdlrNeedsCons(SCIP_CONSHDLR *conshdlr)
Definition: cons.c:5302
SCIP_RETCODE SCIPsetConshdlrCopy(SCIP *scip, SCIP_CONSHDLR *conshdlr, SCIP_DECL_CONSHDLRCOPY((*conshdlrcopy)), SCIP_DECL_CONSCOPY((*conscopy)))
Definition: scip_cons.c:347
void SCIPconshdlrSetNeedsCons(SCIP_CONSHDLR *conshdlr, SCIP_Bool needscons)
Definition: cons.c:5312
SCIP_RETCODE SCIPsetConshdlrInitlp(SCIP *scip, SCIP_CONSHDLR *conshdlr, SCIP_DECL_CONSINITLP((*consinitlp)))
Definition: scip_cons.c:624
SCIP_CONSHDLRDATA * SCIPconshdlrGetData(SCIP_CONSHDLR *conshdlr)
Definition: cons.c:4336
SCIP_HEUR * SCIPfindHeur(SCIP *scip, const char *name)
Definition: scip_heur.c:263
SCIP_LPSOLSTAT SCIPgetLPSolstat(SCIP *scip)
Definition: scip_lp.c:174
#define SCIPfreeBlockMemoryArray(scip, ptr, num)
Definition: scip_mem.h:110
int SCIPcalcMemGrowSize(SCIP *scip, int num)
Definition: scip_mem.c:139
#define SCIPallocMemory(scip, ptr)
Definition: scip_mem.h:60
#define SCIPfreeMemory(scip, ptr)
Definition: scip_mem.h:78
#define SCIPallocBlockMemoryArray(scip, ptr, num)
Definition: scip_mem.h:93
#define SCIPreallocBlockMemoryArray(scip, ptr, oldnum, newnum)
Definition: scip_mem.h:99
SCIP_RETCODE SCIPcreateSolCopy(SCIP *scip, SCIP_SOL **sol, SCIP_SOL *sourcesol)
Definition: scip_sol.c:884
SCIP_RETCODE SCIPfreeSol(SCIP *scip, SCIP_SOL **sol)
Definition: scip_sol.c:1252
SCIP_RETCODE SCIPcreateLPSol(SCIP *scip, SCIP_SOL **sol, SCIP_HEUR *heur)
Definition: scip_sol.c:608
SCIP_RETCODE SCIPunlinkSol(SCIP *scip, SCIP_SOL *sol)
Definition: scip_sol.c:1506
SCIP_Bool SCIPsolIsOriginal(SCIP_SOL *sol)
Definition: sol.c:4140
SCIP_RETCODE SCIPcreateRelaxSol(SCIP *scip, SCIP_SOL **sol, SCIP_HEUR *heur)
Definition: scip_sol.c:699
int SCIPsolGetIndex(SCIP_SOL *sol)
Definition: sol.c:4290
SCIP_RETCODE SCIPcheckSol(SCIP *scip, SCIP_SOL *sol, SCIP_Bool printreason, SCIP_Bool completely, SCIP_Bool checkbounds, SCIP_Bool checkintegrality, SCIP_Bool checklprows, SCIP_Bool *feasible)
Definition: scip_sol.c:4312
SCIP_RETCODE SCIPsetSolVal(SCIP *scip, SCIP_SOL *sol, SCIP_VAR *var, SCIP_Real val)
Definition: scip_sol.c:1571
SCIP_Real SCIPgetSolVal(SCIP *scip, SCIP_SOL *sol, SCIP_VAR *var)
Definition: scip_sol.c:1765
SCIP_RETCODE SCIPcreatePseudoSol(SCIP *scip, SCIP_SOL **sol, SCIP_HEUR *heur)
Definition: scip_sol.c:726
SCIP_Bool SCIPisInfinity(SCIP *scip, SCIP_Real val)
SCIP_Bool SCIPisGT(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
SCIP_Bool SCIPisEQ(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
SCIP_Bool SCIPisLT(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
SCIP_Bool SCIPvarIsActive(SCIP_VAR *var)
Definition: var.c:23642
SCIP_RETCODE SCIPchgVarLb(SCIP *scip, SCIP_VAR *var, SCIP_Real newbound)
Definition: scip_var.c:5697
SCIP_VARSTATUS SCIPvarGetStatus(SCIP_VAR *var)
Definition: var.c:23386
SCIP_RETCODE SCIPaddVarLocksType(SCIP *scip, SCIP_VAR *var, SCIP_LOCKTYPE locktype, int nlocksdown, int nlocksup)
Definition: scip_var.c:5118
const char * SCIPvarGetName(SCIP_VAR *var)
Definition: var.c:23267
SCIP_Real SCIPvarGetLbLocal(SCIP_VAR *var)
Definition: var.c:24234
primal heuristic that tries a given solution
methods commonly used by primal heuristics
void SCIPmessagePrintInfo(SCIP_MESSAGEHDLR *messagehdlr, const char *formatstr,...)
Definition: message.c:594
SCIP callable library.
@ SCIP_BENDERSENFOTYPE_RELAX
Definition: type_benders.h:52
@ SCIP_BENDERSENFOTYPE_LP
Definition: type_benders.h:51
@ SCIP_BENDERSENFOTYPE_CHECK
Definition: type_benders.h:54
@ SCIP_BENDERSENFOTYPE_PSEUDO
Definition: type_benders.h:53
enum SCIP_BendersObjectiveType SCIP_BENDERSOBJTYPE
Definition: type_benders.h:91
@ SCIP_BENDERSOBJTYPE_SUM
Definition: type_benders.h:88
@ SCIP_BENDERSOBJTYPE_MAX
Definition: type_benders.h:89
enum SCIP_BendersEnfoType SCIP_BENDERSENFOTYPE
Definition: type_benders.h:56
struct SCIP_ConshdlrData SCIP_CONSHDLRDATA
Definition: type_cons.h:64
@ SCIP_LPSOLSTAT_UNBOUNDEDRAY
Definition: type_lp.h:46
@ SCIP_DIDNOTRUN
Definition: type_result.h:42
@ SCIP_CUTOFF
Definition: type_result.h:48
@ SCIP_FEASIBLE
Definition: type_result.h:45
@ SCIP_DIDNOTFIND
Definition: type_result.h:44
@ SCIP_SUCCESS
Definition: type_result.h:58
@ SCIP_INFEASIBLE
Definition: type_result.h:46
enum SCIP_Result SCIP_RESULT
Definition: type_result.h:61
@ SCIP_OKAY
Definition: type_retcode.h:42
enum SCIP_Retcode SCIP_RETCODE
Definition: type_retcode.h:63
@ SCIP_STAGE_INITPRESOLVE
Definition: type_set.h:48
@ SCIP_STAGE_SOLVED
Definition: type_set.h:54
@ SCIP_VARSTATUS_FIXED
Definition: type_var.h:54
@ SCIP_LOCKTYPE_MODEL
Definition: type_var.h:141